diff --git a/.config/CredScanSuppressions.json b/.config/CredScanSuppressions.json index ccd35fe83c7..52381cb8b33 100644 --- a/.config/CredScanSuppressions.json +++ b/.config/CredScanSuppressions.json @@ -1,294 +1,294 @@ -{ - "tool": "Credential Scanner", - "suppressions": [ - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "aspnetcore" - ] - }, - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "password" - ] - }, - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "my password" - ] - }, - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "newpassword" - ] - }, - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "testpassword" - ] - }, - { - "_justification": "This is a fake password hash used in test code.", - "placeholder": [ - "AAABAgMEBQYHCAkKCwwNDg\u002BukCEMDf0yyQ29NYubggE=" - ] - }, - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "1qaz!QAZ" - ] - }, - { - "_justification": "This is a fake password used in test code.", - "placeholder": [ - "1qaz@WSX" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/testCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert1.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert2.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert2.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file without private key", - "file": [ - "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3WithoutPrivateKey.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file without private key", - "file": [ - "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCertWithoutPrivateKey.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Middleware/WebSockets/test/ConformanceTests/AutobahnTestApp/TestResources/testCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.FunctionalTest/negotiateAuthCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Servers/IIS/tools/TestCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/aspnetdevcert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/eku.client.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/eku.code_signing.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/eku.multiple_usages.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/eku.server.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/no_extensions.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/SignalR/common/Shared/testCert.pfx" - ] - }, - { - "_justification": "Legitimate UT certificate file with private key", - "file": [ - "/src/aspnetcore/src/SignalR/common/Shared/testCertECC.pfx" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-aspnet.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-dsa-protected.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-dsa.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-ecdsa-protected.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-ecdsa.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-rsa-protected.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/https-rsa.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/intermediate2_ca.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/intermediate_ca.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/leaf.com.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/Shared/TestCertificates/root_ca.key" - ] - }, - { - "_justification": "Legitimate key file used for testing", - "file": [ - "/src/aspnetcore/src/SignalR/clients/ts/FunctionalTests/node_modules/https-proxy-agent/node_modules/agent-base/test/ssl-cert-snakeoil.key" - ] - }, - { - "_justification": "New certificate file with private key used only for testing", - "file": [ - "/src/msbuild/src/Tasks.UnitTests/TestResources/mycert.pfx" - ] - }, - { - "_justification": "Unit tests use dummy input data.", - "file": [ - "/src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", - "/src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/SetCredentialsRequestTests.cs", - "/src/nuget-client/docs/cross-platform-debugging.md" - ] - }, - { - "_justification": "Suppression approved. Private key for testing purpose.", - "placeholder": [ - "-----BEGIN PRIVATE KEY-----", - "-----BEGIN * PRIVATE KEY-----" - ], - "file": [ - "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs", - "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", - "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", - "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs", - "/src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.key" - ] - }, - { - "_justification": "Suppression approved. Private key for testing purpose.", - "file": [ - "/src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx" - ] - }, - { - "_justification": "Third party repositories, no control over the content and these are not actual secrets.", - "file": [ - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA1.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA256.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256_2.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA384.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA512.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", - "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp_TemporaryKey.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner_TemporaryKey.pfx", - "/src/source-build-reference-packages/src/externalPackages/src/xunit/appveyor.yml" - ] - } - ] +{ + "tool": "Credential Scanner", + "suppressions": [ + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "aspnetcore" + ] + }, + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "password" + ] + }, + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "my password" + ] + }, + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "newpassword" + ] + }, + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "testpassword" + ] + }, + { + "_justification": "This is a fake password hash used in test code.", + "placeholder": [ + "AAABAgMEBQYHCAkKCwwNDg\u002BukCEMDf0yyQ29NYubggE=" + ] + }, + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "1qaz!QAZ" + ] + }, + { + "_justification": "This is a fake password used in test code.", + "placeholder": [ + "1qaz@WSX" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/testCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert1.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/TestFiles/TestCert2.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert2.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file without private key", + "file": [ + "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCert3WithoutPrivateKey.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file without private key", + "file": [ + "/src/aspnetcore/src/DataProtection/Extensions/test/TestFiles/TestCertWithoutPrivateKey.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/DefaultBuilder/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Middleware/WebSockets/test/ConformanceTests/AutobahnTestApp/TestResources/testCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Security/Authentication/Negotiate/test/Negotiate.FunctionalTest/negotiateAuthCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Servers/IIS/tools/TestCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/aspnetdevcert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/eku.client.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/eku.code_signing.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/eku.multiple_usages.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/eku.server.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/no_extensions.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/SignalR/common/Shared/testCert.pfx" + ] + }, + { + "_justification": "Legitimate UT certificate file with private key", + "file": [ + "/src/aspnetcore/src/SignalR/common/Shared/testCertECC.pfx" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-aspnet.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-dsa-protected.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-dsa.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-ecdsa-protected.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-ecdsa.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-rsa-protected.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/https-rsa.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/intermediate2_ca.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/intermediate_ca.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/leaf.com.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/Shared/TestCertificates/root_ca.key" + ] + }, + { + "_justification": "Legitimate key file used for testing", + "file": [ + "/src/aspnetcore/src/SignalR/clients/ts/FunctionalTests/node_modules/https-proxy-agent/node_modules/agent-base/test/ssl-cert-snakeoil.key" + ] + }, + { + "_justification": "New certificate file with private key used only for testing", + "file": [ + "/src/msbuild/src/Tasks.UnitTests/TestResources/mycert.pfx" + ] + }, + { + "_justification": "Unit tests use dummy input data.", + "file": [ + "/src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/GetCredentialsResponseTests.cs", + "/src/nuget-client/test/NuGet.Core.Tests/NuGet.Protocol.Tests/Plugins/Messages/SetCredentialsRequestTests.cs", + "/src/nuget-client/docs/cross-platform-debugging.md" + ] + }, + { + "_justification": "Suppression approved. Private key for testing purpose.", + "placeholder": [ + "-----BEGIN PRIVATE KEY-----", + "-----BEGIN * PRIVATE KEY-----" + ], + "file": [ + "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyPemTests.cs", + "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/EC/ECKeyPemTests.cs", + "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyPemTests.cs", + "/src/runtime/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/TestData.cs", + "/src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.key" + ] + }, + { + "_justification": "Suppression approved. Private key for testing purpose.", + "file": [ + "/src/runtime/src/tests/FunctionalTests/Android/Device_Emulator/gRPC/grpc-dotnet/testassets/Certs/InteropTests/server1.pfx" + ] + }, + { + "_justification": "Third party repositories, no control over the content and these are not actual secrets.", + "file": [ + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA1.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned1024_SHA256.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA256_2.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA384.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Certs/SelfSigned2048_SHA512.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs", + "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp/Humanizer.Tests.Uwp_TemporaryKey.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/humanizer/src/Humanizer.Tests.Uwp.Runner/Humanizer.Tests.Uwp.Runner_TemporaryKey.pfx", + "/src/source-build-reference-packages/src/externalPackages/src/xunit/appveyor.yml" + ] + } + ] } \ No newline at end of file diff --git a/src/roslyn/.vscode/tasks.json b/src/roslyn/.vscode/tasks.json index eecd040d5dd..1c6757b1fe5 100644 --- a/src/roslyn/.vscode/tasks.json +++ b/src/roslyn/.vscode/tasks.json @@ -132,8 +132,9 @@ "command": "dotnet", "type": "shell", "args": [ - "pwsh", - "${workspaceFolder}/eng/generate-compiler-code.ps1" + "run", + "--file", + "${workspaceFolder}/eng/generate-compiler-code.cs" ], "problemMatcher": "$msCompile", "group": "build" diff --git a/src/roslyn/Directory.Packages.props b/src/roslyn/Directory.Packages.props index e3e2abfdd7e..9f2e5ffec10 100644 --- a/src/roslyn/Directory.Packages.props +++ b/src/roslyn/Directory.Packages.props @@ -3,5 +3,5 @@ true true - - \ No newline at end of file + + diff --git a/src/roslyn/azure-pipelines-official.yml b/src/roslyn/azure-pipelines-official.yml index b3869f8e8ae..453850201fb 100644 --- a/src/roslyn/azure-pipelines-official.yml +++ b/src/roslyn/azure-pipelines-official.yml @@ -112,6 +112,8 @@ extends: parameters: featureFlags: autoBaseline: true + autoEnablePREfastWithNewRuleset: false + autoEnableRoslynWithNewRuleset: false sdl: sourceAnalysisPool: name: NetCore1ESPool-Svc-Internal @@ -288,13 +290,13 @@ extends: - task: MicroBuildSigningPlugin@4 inputs: - signType: $(SignType) + signType: ${{ parameters.SignType }} zipSources: false feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json # Set ConnectedPMEServiceName if the build is a CI build, otherwise it is not needed ${{ if eq(variables['Build.Reason'], 'IndividualCI') }}: ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca - condition: and(succeeded(), in(variables['SignType'], 'test', 'real')) + condition: and(succeeded(), in('${{ parameters.SignType }}', 'test', 'real')) - download: profilingInputs artifact: merged mibc @@ -322,7 +324,7 @@ extends: /p:RepositoryName=$(Build.Repository.Name) /p:VisualStudioDropName=$(VisualStudio.DropName) /p:VSCodeOptimizationDataRoot="$(VSCodeOptimizationDataRoot)" - /p:DotNetSignType=$(SignType) + /p:DotNetSignType=${{ parameters.SignType }} /p:DotnetPublishUsingPipelines=true /p:IgnoreIbcMergeErrors=true /p:GenerateSbom=true @@ -401,7 +403,7 @@ extends: componentPassword: $(dn-bot-dnceng-build-e-code-full-release-e-packaging-r) componentBuildProjectName: internal sourceBranch: "$(ComponentBranchName)" - publishDataURI: "https://dev.azure.com/dnceng/internal/_apis/git/repositories/dotnet-roslyn/items?path=eng/config/PublishData.json&api-version=6.0" + publishDataURI: "https://dev.azure.com/dnceng/internal/_apis/git/repositories/dotnet-roslyn/items?path=eng/config/PublishData.json&version=$(ComponentBranchName)&api-version=6.0" publishDataAccessToken: "$(System.AccessToken)" dropPath: '$(Pipeline.Workspace)\VSSetup' cherryPick: ${{ parameters.VisualStudioCherryPickSHA }} @@ -410,9 +412,6 @@ extends: - template: /eng/common/templates-official/post-build/post-build.yml@self parameters: publishingInfraVersion: 3 - # Symbol validation is not entirely reliable as of yet, so should be turned off until - # https://github.com/dotnet/arcade/issues/2871 is resolved. - enableSymbolValidation: false enableSourceLinkValidation: false # Enable SDL validation, passing through values from the 'DotNet-Roslyn-SDLValidation-Params' group. SDLValidationParameters: diff --git a/src/roslyn/azure-pipelines-pr-validation.yml b/src/roslyn/azure-pipelines-pr-validation.yml index 427f87827e3..19e627450bd 100644 --- a/src/roslyn/azure-pipelines-pr-validation.yml +++ b/src/roslyn/azure-pipelines-pr-validation.yml @@ -357,7 +357,7 @@ extends: vsBranchName: ${{ parameters.VisualStudioBranchName }} titlePrefix: ${{ parameters.OptionalTitlePrefix }} sourceBranch: $(ComponentBranchName) - publishDataURI: "https://raw.githubusercontent.com/dotnet/roslyn/main/eng/config/PublishData.json" + publishDataURI: "https://raw.githubusercontent.com/dotnet/roslyn/$(ComponentBranchName)/eng/config/PublishData.json" queueSpeedometerValidation: true dropPath: '$(Pipeline.Workspace)\VSSetup' retainInsertedBuild: false diff --git a/src/roslyn/azure-pipelines.yml b/src/roslyn/azure-pipelines.yml index d2d30e15a4a..df8d9cb182a 100644 --- a/src/roslyn/azure-pipelines.yml +++ b/src/roslyn/azure-pipelines.yml @@ -125,6 +125,10 @@ parameters: default: name: $(PoolName) demands: ImageOverride -equals $(WindowsQueueName) + - name: macOSPool + type: object + default: + vmImage: macOS-15 - name: vs2022PreviewPool type: object default: @@ -170,6 +174,16 @@ stages: testArtifactName: Transport_Artifacts_Unix_Debug poolParameters: ${{ parameters.ubuntuPool }} +- ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: + - stage: MacOS_Build + dependsOn: [] + jobs: + - template: eng/pipelines/build-unix-job.yml + parameters: + jobName: Build_macOS_Debug + testArtifactName: Transport_Artifacts_macOS_Debug + poolParameters: ${{ parameters.macOSPool }} + - stage: Source_Build dependsOn: [] jobs: @@ -370,14 +384,19 @@ stages: testArguments: --testCoreClr poolParameters: ${{ parameters.ubuntuPool }} - # https://github.com/dotnet/runtime/issues/97186 - # Disabled until runtime can track down the crash - - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: +- ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: + - stage: MacOS_Debug_CoreClr + dependsOn: MacOS_Build + variables: + - ${{ if ne(variables['System.TeamProject'], 'public') }}: + - group: DotNet-HelixApi-Access + jobs: + # https://github.com/dotnet/runtime/issues/97186 - template: eng/pipelines/test-unix-job.yml parameters: testRunName: 'Test macOS Debug' jobName: Test_macOS_Debug - testArtifactName: Transport_Artifacts_Unix_Debug + testArtifactName: Transport_Artifacts_macOS_Debug configuration: Debug testArguments: --testCoreClr helixQueueName: $(HelixMacOsQueueName) @@ -446,8 +465,12 @@ stages: - powershell: eng/validate-rules-missing-documentation.ps1 -ci displayName: Validate rules missing documentation - - powershell: eng/generate-compiler-code.ps1 -test -configuration Release + - task: DotNetCoreCLI@2 displayName: Generate Syntax Files + inputs: + command: 'custom' + custom: 'run' + arguments: '--file $(Build.SourcesDirectory)/eng/generate-compiler-code.cs -- -test -configuration Release' - powershell: eng/validate-code-formatting.ps1 -ci -rootDirectory $(Build.SourcesDirectory)\src -includeDirectories Compilers\CSharp\Portable\Generated\, Compilers\VisualBasic\Portable\Generated\, ExpressionEvaluator\VisualBasic\Source\ResultProvider\Generated\ displayName: Validate Generated Syntax Files diff --git a/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index 816c2435e2a..53e5dc91ef7 100644 --- a/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/src/roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -7,7 +7,7 @@ This document lists known breaking changes in Roslyn after .NET 9 general releas ***Introduced in Visual Studio 2022 version 17.13*** C# 14 introduces the ability to write a lambda with parameter modifiers, without having to specify a parameter type: -https://github.com/dotnet/csharplang/blob/main/proposals/simple-lambda-parameters-with-modifiers.md +[Simple lambda parameters with modifiers](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-14.0/simple-lambda-parameters-with-modifiers.md) As part of this work, a breaking change was accepted where `scoped` will always be treated as a modifier in a lambda parameter, even where it might have been accepted as a type name in the past. For example: @@ -371,7 +371,7 @@ class C { } // type parameter may not be named "extension" ## Partial properties and events are now implicitly virtual and public -***Introduced in Visual Studio 2022 version 17.15*** +***Introduced in Visual Studio 2026 version 18.0 preview 1*** We have fixed [an inconsistency](https://github.com/dotnet/roslyn/issues/77346) where partial interface properties and events would not be implicitly `virtual` and `public` unlike their non-partial equivalents. @@ -407,3 +407,30 @@ partial interface I class C : I; ``` + +## Missing `ParamCollectionAttribute` is reported in more cases + +***Introduced in Visual Studio 2026 version 18.0*** + +If you are compiling a `.netmodule` (note that this doesn't apply to normal DLL/EXE compilations), +and have a lambda or a local function with a `params` collection parameter, +and the `ParamCollectionAttribute` is not found, a compilation error is now reported +(because the attribute now must be [emitted](https://github.com/dotnet/roslyn/issues/79752) on the synthesized method +but the attribute type itself is not synthesized by the compiler into a `.netmodule`). +You can work around that by defining the attribute yourself. + +```cs +using System; +using System.Collections.Generic; +class C +{ + void M() + { + Func, int> lam = (params IList xs) => xs.Count; // error if ParamCollectionAttribute does not exist + lam([1, 2, 3]); + + int func(params IList xs) => xs.Count; // error if ParamCollectionAttribute does not exist + func(4, 5, 6); + } +} +``` diff --git a/src/roslyn/docs/contributing/Compiler Test Plan.md b/src/roslyn/docs/contributing/Compiler Test Plan.md index bc9d292891d..00098c2fa22 100644 --- a/src/roslyn/docs/contributing/Compiler Test Plan.md +++ b/src/roslyn/docs/contributing/Compiler Test Plan.md @@ -9,7 +9,7 @@ This document provides guidance for thinking about language interactions and tes - BCL (including mono) and other customer impact - Determinism - Loading from metadata (source vs. loaded from metadata) -- Public interface of compiler APIs (including semantic model APIs listed below): +- Public compiler APIs (including semantic model and other APIs listed below): - GetDeclaredSymbol - GetEnclosingSymbol - GetSymbolInfo @@ -29,12 +29,21 @@ This document provides guidance for thinking about language interactions and tes - ClassifyConversion - GetOperation (`IOperation`) - GetCFG (`ControlFlowGraph`) + - DocumentationCommentId APIs - VB/F# interop - C++/CLI interop (particularly for metadata format changes, e.g. DIMs, static abstracts in interfaces, or generic attributes) - Performance and stress testing - Can build VS - Check that `Obsolete` is honored for members used in binding/lowering - LangVersion + +- Does the feature use cryptographic hashes in any way? (examples: metadata names of file-local types, extension types, assembly strong naming, PDB document table, etc.) + - Consider using non-cryptographic hash such as `XxHash128` instead. + - If you must use a cryptographic hash in the feature implementation, then use `SourceHashAlgorithms.Default`, and not any specific hash. + - A cryptographic hash must never be included in a public API name. Taking a change to the default crypto algorithm would then change public API surface, which would be enormously breaking. + - **DO NOT** allow using the value of a crypto hash in a field, method or type name + - **DO** allow using the value of a crypto hash in attribute or field values + - Any time the compiler reads in metadata containing crypto hashes, even if it's an attribute value, ensure the crypto hash algorithm name is included in the metadata (e.g. prefixing it to the hash value), so that it can be changed over time and the compiler can continue to read both metadata using both the old and new algorithms. # Type and members - Access modifiers (public, protected, internal, protected internal, private protected, private), static, ref diff --git a/src/roslyn/eng/Directory.Packages.props b/src/roslyn/eng/Directory.Packages.props index 1e8a1d46394..3ed04d2188b 100644 --- a/src/roslyn/eng/Directory.Packages.props +++ b/src/roslyn/eng/Directory.Packages.props @@ -1,339 +1,6 @@ - - - - 3.11.0-beta1.24081.1 - 1.1.3-beta1.24319.1 - 0.1.800-beta - <_BasicReferenceAssembliesVersion>1.8.3 - - 4.8.0-3.final - 18.0.332-preview - 9.0.0-rc.2.24462.10 - 6.0.0-rtm.21518.12 - 7.0.0-alpha.1.22060.1 - - 8.0.10 - 8.0.10 - <_xunitVersion>2.9.2 - 2.1.0 - - 2.14.1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Please remove this file during conflict resolution in your PR. +This file has been reverted (removed) in the source repository but the PR branch +does not have the file yet as it's based on an older commit. This means the file is +not getting removed in the PR due to the other conflicts. \ No newline at end of file diff --git a/src/roslyn/eng/Packages.props b/src/roslyn/eng/Packages.props new file mode 100644 index 00000000000..37ee560eb8a --- /dev/null +++ b/src/roslyn/eng/Packages.props @@ -0,0 +1,341 @@ + + + + 3.11.0-beta1.24081.1 + 1.1.3-beta1.24319.1 + 0.1.800-beta + <_BasicReferenceAssembliesVersion>1.8.3 + + 4.8.0-3.final + 18.0.332-preview + 9.0.0-rc.2.24462.10 + 6.0.0-rtm.21518.12 + 7.0.0-alpha.1.22060.1 + + 8.0.10 + 8.0.10 + <_xunitVersion>2.9.2 + 2.1.0 + + 2.14.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/roslyn/eng/Version.Details.props b/src/roslyn/eng/Version.Details.props index 964378c8f4f..5414f6197a8 100644 --- a/src/roslyn/eng/Version.Details.props +++ b/src/roslyn/eng/Version.Details.props @@ -9,7 +9,7 @@ This file should be imported by eng/Versions.props 3.11.0 4.10.0-1.24061.4 - 2.0.0-rc.2.25427.104 + 2.0.0-rc.2.25468.104 9.0.0 9.0.0 @@ -38,9 +38,9 @@ This file should be imported by eng/Versions.props 9.0.0 9.0.0 - 10.0.0-beta.25427.2 - 10.0.0-beta.25427.2 - 10.0.0-beta.25427.2 + 11.0.0-beta.25469.3 + 11.0.0-beta.25469.3 + 11.0.0-beta.25469.3 2.0.0 diff --git a/src/roslyn/eng/Version.Details.xml b/src/roslyn/eng/Version.Details.xml index 6792bd1be72..25c49fa8f16 100644 --- a/src/roslyn/eng/Version.Details.xml +++ b/src/roslyn/eng/Version.Details.xml @@ -1,15 +1,15 @@ - + https://github.com/dotnet/roslyn ae1fff344d46976624e68ae17164e0607ab68b10 - + https://github.com/dotnet/dotnet - 7ac1ca67bb1fb8a381c1c94a9f82a97725f0ccf3 + 2dea164f01d307c409cfe0d0ee5cb8a0691e3c94 @@ -115,13 +115,13 @@ - + https://github.com/dotnet/arcade - ee3cae9ed3ef1990505e891831163ef34220d4e0 + dbc4dce57f6a2087f13d86e89dfb0334be3c42e2 - + https://github.com/dotnet/arcade - ee3cae9ed3ef1990505e891831163ef34220d4e0 + dbc4dce57f6a2087f13d86e89dfb0334be3c42e2 https://github.com/dotnet/symreader @@ -131,9 +131,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - ee3cae9ed3ef1990505e891831163ef34220d4e0 + dbc4dce57f6a2087f13d86e89dfb0334be3c42e2 https://github.com/dotnet/roslyn-analyzers diff --git a/src/roslyn/eng/Versions.props b/src/roslyn/eng/Versions.props index aec4f310cca..25a52871980 100644 --- a/src/roslyn/eng/Versions.props +++ b/src/roslyn/eng/Versions.props @@ -100,6 +100,17 @@ + + + 17.10.29 + 8.0.5 + true false diff --git a/src/roslyn/eng/build-utils.ps1 b/src/roslyn/eng/build-utils.ps1 index 64dd3a3d343..ba679ba8beb 100644 --- a/src/roslyn/eng/build-utils.ps1 +++ b/src/roslyn/eng/build-utils.ps1 @@ -8,7 +8,6 @@ $ErrorActionPreference="Stop" $VSSetupDir = Join-Path $ArtifactsDir "VSSetup\$configuration" $PackagesDir = Join-Path $ArtifactsDir "packages\$configuration" -$PublishDataUrl = "https://raw.githubusercontent.com/dotnet/roslyn/main/eng/config/PublishData.json" $binaryLog = if (Test-Path variable:binaryLog) { $binaryLog } else { $false } $nodeReuse = if (Test-Path variable:nodeReuse) { $nodeReuse } else { $false } @@ -27,8 +26,10 @@ function GetPublishData() { return $global:_PublishData } - Write-Host "Downloading $PublishDataUrl" - $content = (Invoke-WebRequest -Uri $PublishDataUrl -UseBasicParsing).Content + $publishDataFile = Join-Path $PSScriptRoot "config\PublishData.json" + + Write-Host "Reading $publishDataFile" + $content = Get-Content -Path $publishDataFile -Raw return $global:_PublishData = ConvertFrom-Json $content } diff --git a/src/roslyn/eng/build.sh b/src/roslyn/eng/build.sh index caf46e4825c..a87e4b2a131 100755 --- a/src/roslyn/eng/build.sh +++ b/src/roslyn/eng/build.sh @@ -341,6 +341,7 @@ function GetCompilerTestAssembliesIncludePaths { assemblies+=" --include '^Microsoft\.CodeAnalysis\.VisualBasic\.Emit\.UnitTests$'" assemblies+=" --include '^Roslyn\.Compilers\.VisualBasic\.IOperation\.UnitTests$'" assemblies+=" --include '^Microsoft\.CodeAnalysis\.VisualBasic\.CommandLine\.UnitTests$'" + assemblies+=" --include '^Microsoft\.Build\.Tasks\.CodeAnalysis\.UnitTests$'" echo "$assemblies" } diff --git a/src/roslyn/eng/common/SetupNugetSources.ps1 b/src/roslyn/eng/common/SetupNugetSources.ps1 index 792b60b49d4..9445c314325 100644 --- a/src/roslyn/eng/common/SetupNugetSources.ps1 +++ b/src/roslyn/eng/common/SetupNugetSources.ps1 @@ -157,7 +157,7 @@ if ($dotnet31Source -ne $null) { AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password } -$dotnetVersions = @('5','6','7','8','9') +$dotnetVersions = @('5','6','7','8','9','10') foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; diff --git a/src/roslyn/eng/common/SetupNugetSources.sh b/src/roslyn/eng/common/SetupNugetSources.sh index facb415ca6f..ddf4efc81a4 100644 --- a/src/roslyn/eng/common/SetupNugetSources.sh +++ b/src/roslyn/eng/common/SetupNugetSources.sh @@ -99,7 +99,7 @@ if [ "$?" == "0" ]; then PackageSources+=('dotnet3.1-internal-transport') fi -DotNetVersions=('5' '6' '7' '8' '9') +DotNetVersions=('5' '6' '7' '8' '9' '10') for DotNetVersion in ${DotNetVersions[@]} ; do FeedPrefix="dotnet${DotNetVersion}"; diff --git a/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml b/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml index 348cd16376f..37dff559fc1 100644 --- a/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml +++ b/src/roslyn/eng/common/core-templates/job/publish-build-assets.yml @@ -40,6 +40,8 @@ parameters: repositoryAlias: self + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -62,6 +64,11 @@ jobs: value: false # unconditional - needed for logs publishing (redactor tool version) - template: /eng/common/core-templates/post-build/common-variables.yml + - name: OfficialBuildId + ${{ if ne(parameters.officialBuildId, '') }}: + value: ${{ parameters.officialBuildId }} + ${{ else }}: + value: $(Build.BuildNumber) pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) @@ -124,7 +131,7 @@ jobs: /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} diff --git a/src/roslyn/eng/common/core-templates/jobs/jobs.yml b/src/roslyn/eng/common/core-templates/jobs/jobs.yml index b637cb6e948..01ada747665 100644 --- a/src/roslyn/eng/common/core-templates/jobs/jobs.yml +++ b/src/roslyn/eng/common/core-templates/jobs/jobs.yml @@ -44,6 +44,7 @@ parameters: artifacts: {} is1ESPipeline: '' repositoryAlias: self + officialBuildId: '' # 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. @@ -116,3 +117,4 @@ jobs: artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml b/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml index c05f6502797..003f7eae0fa 100644 --- a/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml +++ b/src/roslyn/eng/common/core-templates/steps/generate-sbom.yml @@ -5,7 +5,7 @@ # IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. parameters: - PackageVersion: 10.0.0 + PackageVersion: 11.0.0 BuildDropPath: '$(System.DefaultWorkingDirectory)/artifacts' PackageName: '.NET' ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom diff --git a/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml b/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml index e9a694afa58..eff4573c6e5 100644 --- a/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/src/roslyn/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,6 +1,6 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250818.1 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250818.1 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog diff --git a/src/roslyn/eng/config/PublishData.json b/src/roslyn/eng/config/PublishData.json index 3e379f2ceef..120ca91da2c 100644 --- a/src/roslyn/eng/config/PublishData.json +++ b/src/roslyn/eng/config/PublishData.json @@ -14,6 +14,7 @@ "Microsoft.CodeAnalysis.Workspaces.Common": "vssdk", "Microsoft.CodeAnalysis.Workspaces.MSBuild": "arcade", "Microsoft.CodeAnalysis.Workspaces.Desktop": "arcade", + "Microsoft.CodeAnalysis.Workspaces.Test.Utilities": "vs-impl", "Microsoft.CodeAnalysis.Compiler.Test.Resources": "vs-impl", "Microsoft.CodeAnalysis.CSharp": "vssdk", "Microsoft.CodeAnalysis.CSharp.CodeStyle": "arcade", diff --git a/src/roslyn/eng/generate-compiler-code.cmd b/src/roslyn/eng/generate-compiler-code.cmd index 60a78f05898..c16c34740ce 100644 --- a/src/roslyn/eng/generate-compiler-code.cmd +++ b/src/roslyn/eng/generate-compiler-code.cmd @@ -1,4 +1,3 @@ @echo off -set PSMODULEPATH= -powershell -noprofile -executionPolicy Unrestricted -file "%~dp0\generate-compiler-code.ps1" %* +call "%~dp0\common\dotnet.cmd" run --file "%~dp0\generate-compiler-code.cs" %* diff --git a/src/roslyn/eng/generate-compiler-code.cs b/src/roslyn/eng/generate-compiler-code.cs new file mode 100755 index 00000000000..855fcc78a88 --- /dev/null +++ b/src/roslyn/eng/generate-compiler-code.cs @@ -0,0 +1,242 @@ +#!/usr/bin/env dotnet +// 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. + +// Need this fix to delete the static graph disable: https://github.com/dotnet/sdk/pull/50532, 10.0.100-rc.2 +#:property RestoreUseStaticGraphEvaluation=false +#:project ../src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/ +#:project ../src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/ +#:project ../src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/ +#:project ../src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/ +#:project ../src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/ +#:project ../src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/ + +using System.Runtime.CompilerServices; + +var (test, configuration, repoRoot) = ParseArgs(args); + +// Folders for test artifacts when running in -test mode +var generationTempDirRoot = Path.Join(repoRoot, "artifacts", "log", configuration, "Generated"); +var retVal = 0; + +// Generate C# source +Console.WriteLine("Generating C# files..."); + +var csharpLocation = GetLanguageDirStructure(repoRoot, "CSharp", generationTempDirRoot, test); +retVal = GenerateLanguage( + BoundTreeGenerator.TargetLanguage.CSharp, + csharpLocation.LanguageDir, + test ? csharpLocation.GeneratedSourceTempDir : csharpLocation.GeneratedSourceDir, + (test ? csharpLocation.GeneratedTestTempDir : csharpLocation.GeneratedTestDir)!, + (inputFile, outputFile, writeSource, writeTests, writeGrammar) + => CSharpSyntaxGenerator.Program.Generate(inputFile, outputFile, writeSource, writeTests, writeGrammar, writeSignatures: false), + Microsoft.CodeAnalysis.CSharp.Internal.CSharpErrorFactsGenerator.Program.Generate); + +// Generate VB source +Console.WriteLine("Generating VB files..."); + +var vbLocation = GetLanguageDirStructure(repoRoot, "VisualBasic", generationTempDirRoot, test); +retVal |= GenerateLanguage( + BoundTreeGenerator.TargetLanguage.VB, + vbLocation.LanguageDir, + test ? vbLocation.GeneratedSourceTempDir : vbLocation.GeneratedSourceDir, + (test ? vbLocation.GeneratedTestTempDir : vbLocation.GeneratedTestDir)!, + Microsoft.CodeAnalysis.VisualBasic.Internal.VBSyntaxGenerator.Program.Generate, + Microsoft.CodeAnalysis.VisualBasic.Internal.VBErrorFactsGenerator.Program.Generate); + +// Generate VB GetText source +Console.WriteLine("Generating VB GetText files..."); + +var getTextLocation = GetGetTextLocation(vbLocation, repoRoot, generationTempDirRoot, test); +Microsoft.CodeAnalysis.VisualBasic.Internal.VBSyntaxGenerator.Program.Generate( + Path.Join(vbLocation.LanguageDir, "Syntax", "Syntax.xml"), + Path.Join(test ? getTextLocation.GeneratedSourceTempDir : getTextLocation.GeneratedSourceDir, "Syntax.xml.GetText.Generated.vb"), + writeSource: false, writeTests: false, writeGrammar: false); + +// Generate IOperation source +Console.WriteLine("Generating IOperation files..."); + +var ioperationLocation = GetIOperationLocation(repoRoot, generationTempDirRoot, test); +retVal |= IOperationGenerator.Program.Generate(Path.Join(ioperationLocation.LanguageDir, "Operations", "OperationInterfaces.xml"), test ? ioperationLocation.GeneratedSourceTempDir : ioperationLocation.GeneratedSourceDir); + +if (test) +{ + Console.WriteLine("Verifying generated files..."); + retVal |= TestGenerationResults(repoRoot, csharpLocation, vbLocation, getTextLocation, ioperationLocation); +} + +Console.WriteLine(retVal == 0 ? "Generation succeeded." : "Generation failed."); +return retVal; + +static (bool test, string configuration, string roslynDirectory) ParseArgs(string[] args, [CallerFilePath] string sourceFilePath = "") +{ + var test = false; + var configuration = "Debug"; + + for (var i = 0; i < args.Length; i++) + { + var a = args[i]; + switch (a) + { + case "-test": + test = true; + break; + case "-configuration" when i + 1 < args.Length: + configuration = args[i++]; + break; + default: + Console.WriteLine("Unknown argument: " + a); + Environment.Exit(1); + break; + } + } + + if (Path.GetDirectoryName(sourceFilePath) is not string engDir || Path.GetDirectoryName(engDir) is not string roslynRoot || !File.Exists(Path.Join(roslynRoot, "eng", Path.GetFileName(sourceFilePath)))) + { + Console.WriteLine("Could not determine source file path. This script must be located in the 'eng' directory of the Roslyn repo."); + Environment.Exit(1); + throw null!; + } + + return (test, configuration, roslynRoot); +} + +static GenerationLocation GetLanguageDirStructure(string repoRoot, string languageName, string generationTempDirRoot, bool isTest) +{ + var languageDir = Path.Join(repoRoot, "src", "Compilers", languageName, "Portable"); + var loc = new GenerationLocation( + languageDir, + Path.Join(languageDir, "Generated"), + Path.Join(repoRoot, "src", "Compilers", languageName, "Test", "Syntax", "Generated"), + Path.Join(generationTempDirRoot, languageName, "Src"), + Path.Join(generationTempDirRoot, languageName, "Test") + ); + + if (isTest) + { + Directory.CreateDirectory(loc.GeneratedSourceTempDir); + Directory.CreateDirectory(loc.GeneratedTestTempDir!); + } + + return loc; +} + +static GenerationLocation GetGetTextLocation(GenerationLocation vbLocation, string repoRoot, string generationTempDirRoot, bool test) +{ + var getTextDir = new GenerationLocation( + vbLocation.LanguageDir, + Path.Join(repoRoot, "src", "ExpressionEvaluator", "VisualBasic", "Source", "ResultProvider", "Generated"), + GeneratedTestDir: null, + Path.Join(generationTempDirRoot, "VisualBasic", "GetText"), + GeneratedTestTempDir: null + ); + + if (test) + { + Directory.CreateDirectory(getTextDir.GeneratedSourceTempDir); + } + + return getTextDir; +} + +static GenerationLocation GetIOperationLocation(string repoRoot, string generationTempDirRoot, bool test) +{ + var languageDir = Path.Join(repoRoot, "src", "Compilers", "Core", "Portable"); + var loc = new GenerationLocation( + languageDir, + Path.Join(languageDir, "Generated"), + GeneratedTestDir: null, + Path.Join(generationTempDirRoot, "Core", "Src"), + GeneratedTestTempDir: null + ); + + if (test) + { + Directory.CreateDirectory(loc.GeneratedSourceTempDir); + } + + return loc; +} + +static int GenerateLanguage(BoundTreeGenerator.TargetLanguage language, string languageDir, string generatedDir, string generatedTestDir, SyntaxGeneratorAction syntaxGenerator, ErrorFactsGeneratorAction errorFactsGenerator) +{ + var extension = language == BoundTreeGenerator.TargetLanguage.CSharp ? "cs" : "vb"; + var generatedTestFile = Path.Join(generatedTestDir, $"Syntax.Test.xml.Generated.{extension}"); + var errorsPath = Path.Join(languageDir, "Errors", language == BoundTreeGenerator.TargetLanguage.CSharp ? "ErrorCode.cs" : "Errors.vb"); + var errorFactsFilePath = Path.Join(generatedDir, $"ErrorFacts.Generated.{extension}"); + + var syntaxFile = Path.Join(languageDir, "Syntax", $"Syntax.xml"); + + int retVal = 0; + + if (language != BoundTreeGenerator.TargetLanguage.CSharp) + { + retVal |= syntaxGenerator(syntaxFile, generatedDir, writeSource: true, writeTests: false, writeGrammar: false); + } + + retVal |= syntaxGenerator(syntaxFile, generatedDir, writeSource: false, writeTests: false, writeGrammar: true); + retVal |= syntaxGenerator(syntaxFile, generatedTestFile, writeSource: false, writeTests: true, writeGrammar: false); + + retVal |= BoundTreeGenerator.Program.Generate( + language, + Path.Join(languageDir, "BoundTree", "BoundNodes.xml"), + Path.Join(generatedDir, $"BoundNodes.xml.Generated.{extension}")); + + retVal |= errorFactsGenerator(errorsPath, errorFactsFilePath); + + return retVal; +} + +static int TestGenerationResults(string repoRoot, GenerationLocation csharpLocation, GenerationLocation vbLocation, GenerationLocation getTextLocation, GenerationLocation ioperationLocation) +{ + string[] actualDirs = [.. csharpLocation.GetActualDirs(), .. vbLocation.GetActualDirs(), getTextLocation.GeneratedSourceDir, ioperationLocation.GeneratedSourceDir]; + string[] tempDirs = [.. csharpLocation.GetTempDirs(), .. vbLocation.GetTempDirs(), getTextLocation.GeneratedSourceTempDir, ioperationLocation.GeneratedSourceTempDir]; + + var retVal = 0; + foreach (var (actualDir, tempDir) in actualDirs.Zip(tempDirs)) + { + var tempFiles = Directory.GetFiles(tempDir).Select(Path.GetFileName); + foreach (var fileName in tempFiles) + { + Console.WriteLine($"Verifying {fileName}"); + var actualFile = Path.Join(actualDir, fileName); + var tempFile = Path.Join(tempDir, fileName); + + if (!File.Exists(actualFile)) + { + Console.WriteLine($"Generated file '{actualFile}' does not exist."); + Console.WriteLine($"Run {Path.Join(repoRoot, "eng", "generate-compiler-code.cmd")} to update it."); + retVal = 1; + } + else if (!File.ReadAllText(actualFile).Equals(File.ReadAllText(tempFile), StringComparison.Ordinal)) + { + Console.WriteLine($"Generated file '{actualFile}' is out of date."); + Console.WriteLine($"Run {Path.Join(repoRoot, "eng", "generate-compiler-code.cmd")} to update it."); + retVal = 1; + } + } + } + + return retVal; +} + +delegate int SyntaxGeneratorAction(string inputFile, string outputFile, bool writeSource, bool writeTests, bool writeGrammar); +delegate int ErrorFactsGeneratorAction(string inputPath, string outputPath); + +readonly record struct GenerationLocation(string LanguageDir, string GeneratedSourceDir, string? GeneratedTestDir, string GeneratedSourceTempDir, string? GeneratedTestTempDir) +{ + public readonly IEnumerable GetActualDirs() + { + yield return GeneratedSourceDir; + if (GeneratedTestDir is not null) + yield return GeneratedTestDir; + } + + public readonly IEnumerable GetTempDirs() + { + yield return GeneratedSourceTempDir; + if (GeneratedTestTempDir is not null) + yield return GeneratedTestTempDir; + } +} diff --git a/src/roslyn/eng/generate-compiler-code.ps1 b/src/roslyn/eng/generate-compiler-code.ps1 deleted file mode 100644 index dee6b02db82..00000000000 --- a/src/roslyn/eng/generate-compiler-code.ps1 +++ /dev/null @@ -1,149 +0,0 @@ -# The compiler source base has a large body of generated code. This script is responsible -# for both generating this code and verifying the generated code is always up to date with -# the generator source files. -[CmdletBinding(PositionalBinding=$false)] -param ([string]$configuration = "Debug", - [switch]$test = $false, - [switch]$ci = $false) - -Set-StrictMode -version 2.0 -$ErrorActionPreference="Stop" - -function Run-Tool($projectFilePath, $toolArgs, $targetFramework) { - $toolName = Split-Path -leaf $projectFilePath - Write-Host "Running $toolName $toolArgs" - Exec-DotNet "run --project $projectFilePath --framework $targetFramework $toolArgs" -} - -function Run-LanguageCore($language, $languageSuffix, $languageDir, $syntaxProject, $errorFactsProject, $generatedDir, $generatedTestDir) { - $syntaxFilePath = Join-Path $languageDir "Syntax\Syntax.xml" - $syntaxTestFilePath = Join-Path $generatedTestDir "Syntax.Test.xml.Generated.$($languageSuffix)" - $boundFilePath = Join-Path $languageDir "BoundTree\BoundNodes.xml" - $boundGeneratedFilePath = Join-Path $generatedDir "BoundNodes.xml.Generated.$($languageSuffix)" - $errorFileName = if ($language -eq "CSharp") { "ErrorCode.cs" } else { "Errors.vb" } - $errorFilePath = Join-Path $languageDir "Errors\$errorFileName" - $errorGeneratedFilePath = Join-Path $generatedDir "ErrorFacts.Generated.$($languageSuffix)" - $targetFramework = "net9.0" - - Create-Directory $generatedDir - Create-Directory $generatedTestDir - - # The C# syntax is now generated by a source generator - if ($language -ne "CSharp") { - Run-Tool $syntaxProject "`"$syntaxFilePath`" `"$generatedDir`"" $targetFramework - } - Run-Tool $syntaxProject "`"$syntaxFilePath`" `"$generatedDir`" /grammar" $targetFramework - Run-Tool $syntaxProject "`"$syntaxFilePath`" `"$syntaxTestFilePath`" /test" $targetFramework - Run-Tool $boundTreeGenProject "$language `"$boundFilePath`" `"$boundGeneratedFilePath`"" $targetFramework - Run-Tool $errorFactsProject "`"$errorFilePath`" `"$errorGeneratedFilePath`"" $targetFramework -} - -# Test the contents of our generated files to ensure they are equal. Compares our checked -# in code with the freshly generated code. -function Test-GeneratedContent($generatedDir, $scratchDir) { - $algo = "MD5" - foreach ($fileName in (Get-ChildItem $scratchDir | Select-Object -ExpandProperty Name)) { - Write-Host "Checking $fileName" - $realFilePath = Join-Path $generatedDir $fileName - $scratchFilePath = Join-Path $scratchDir $fileName - $scratchHash = (Get-FileHash $scratchFilePath -algorithm $algo).Hash - $realHash = (Get-FileHash $realFilePath -algorithm $algo).Hash - if ($scratchHash -ne $realHash) { - Write-Host "Files are out of date" - Write-Host "Run $(Join-Path $PSScriptRoot generate-compiler-code.ps1) to refresh" - throw "Files are out of date" - } - } -} - -function Run-Language($language, $languageSuffix, $languageDir, $languageTestDir, $syntaxTool, $errorFactsTool) { - $generatedDir = Join-Path $languageDir "Generated" - $generatedTestDir = Join-Path $languageTestDir "Generated" - if (-not $test) { - Run-LanguageCore $language $languageSuffix $languageDir $syntaxTool $errorFactsTool $generatedDir $generatedTestDir - } - else { - $scratchDir = Join-Path $generationTempDir "$language\Src" - $scratchTestDir = Join-Path $generationTempDir "$language\Test" - Run-LanguageCore $language $languageSuffix $languageDir $syntaxTool $errorFactsTool $scratchDir $scratchTestDir - Test-GeneratedContent $generatedDir $scratchDir - Test-GeneratedContent $generatedTestDir $scratchTestDir - } -} - -function Run-IOperation($coreDir, $ioperationProject) { - $operationsDir = Join-Path $coreDir "Operations" - $operationsXml = Join-Path $operationsDir "OperationInterfaces.xml" - $generationDir = Join-Path $coreDir "Generated" - $targetFramework = "net9.0" - - if (-not $test) { - Run-Tool $ioperationProject "`"$operationsXml`" `"$generationDir`"" $targetFramework - } else { - $scratchDir = Join-Path $generationTempDir "Core\Operations" - Create-Directory $scratchDir - Run-Tool $ioperationProject "`"$operationsXml`" `"$scratchDir`"" $targetFramework - Test-GeneratedContent $generationDir $scratchDir - } -} - -function Run-GetTextCore($generatedDir) { - $syntaxFilePath = Join-Path $basicDir "Syntax\Syntax.xml" - $syntaxTextFilePath = Join-Path $generatedDir "Syntax.xml.GetText.Generated.vb" - - Create-Directory $generatedDir - Run-Tool $basicSyntaxProject "`"$syntaxFilePath`" `"$syntaxTextFilePath`" /gettext" "net9.0" -} - -function Run-GetText() { - $generatedDir = Join-Path $RepoRoot "src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\Generated" - if (-not $test) { - Run-GetTextCore $generatedDir - } - else { - $scratchDir = Join-Path $generationTempDir "VB\GetText" - Run-GetTextCore $scratchDir - Test-GeneratedContent $generatedDir $scratchDir - } -} - -function Get-ToolPath($projectRelativePath) { - $p = Join-Path 'src\Tools\Source\CompilerGeneratorTools\Source' $projectRelativePath - $p = Join-Path $RepoRoot $p - return $p -} - -try { - . (Join-Path $PSScriptRoot "build-utils.ps1") - Push-Location $RepoRoot - $prepareMachine = $ci - - $dotnet = Ensure-DotnetSdk - $boundTreeGenProject = Get-ToolPath 'BoundTreeGenerator\CompilersBoundTreeGenerator.csproj' - - $coreDir = Join-Path $RepoRoot "src\Compilers\Core\Portable" - $operationsProject = Get-ToolPath "IOperationGenerator\CompilersIOperationGenerator.csproj" - $csharpDir = Join-Path $RepoRoot "src\Compilers\CSharp\Portable" - $csharpTestDir = Join-Path $RepoRoot "src\Compilers\CSharp\Test\Syntax" - $csharpSyntaxProject = Get-ToolPath 'CSharpSyntaxGenerator\CSharpSyntaxGenerator.csproj' - $csharpErrorFactsProject = Get-ToolPath 'CSharpErrorFactsGenerator\CSharpErrorFactsGenerator.csproj' - $basicDir = Join-Path $RepoRoot "src\Compilers\VisualBasic\Portable" - $basicTestDir = Join-Path $RepoRoot "src\Compilers\VisualBasic\Test\Syntax" - $basicSyntaxProject = Get-ToolPath 'VisualBasicSyntaxGenerator\VisualBasicSyntaxGenerator.vbproj' - $basicErrorFactsProject = Get-ToolPath 'VisualBasicErrorFactsGenerator\VisualBasicErrorFactsGenerator.vbproj' - $generationTempDir = Join-Path $RepoRoot "artifacts\log\$configuration\Generated" - - Run-Language "CSharp" "cs" $csharpDir $csharpTestDir $csharpSyntaxProject $csharpErrorFactsProject - Run-Language "VB" "vb" $basicDir $basicTestDir $basicSyntaxProject $basicErrorFactsProject - Run-IOperation $coreDir $operationsProject - Run-GetText - - ExitWithExitCode 0 -} -catch { - Write-Host $_ - ExitWithExitCode 1 -} -finally { - Pop-Location -} diff --git a/src/roslyn/eng/targets/GeneratePkgDef.targets b/src/roslyn/eng/targets/GeneratePkgDef.targets index dff341435fa..d14f08cf197 100644 --- a/src/roslyn/eng/targets/GeneratePkgDef.targets +++ b/src/roslyn/eng/targets/GeneratePkgDef.targets @@ -86,9 +86,18 @@ + + + + + + $(CollectUpToDateCheckInputDesignTimeDependsOn);_SetGeneratePkgDefInputsOutputs + $(CollectUpToDateCheckOutputDesignTimeDependsOn);_SetGeneratePkgDefInputsOutputs + + @@ -293,12 +302,10 @@ - + Encoding="UTF-8" /> diff --git a/src/roslyn/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets b/src/roslyn/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets index 15dfae3b1d9..716f3adda86 100644 --- a/src/roslyn/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets +++ b/src/roslyn/eng/targets/VisualStudio.FastUpToDateCheckWorkarounds.targets @@ -1,22 +1,11 @@ - - - - - - - - - + $(CollectUpToDateCheckInputDesignTimeDependsOn);RemoveBuildOutputSourceItems - <_ItemsInObjDirectory Include="$(IntermediateOutputPath)\**\*" Set="VsixItems" /> diff --git a/src/roslyn/eng/targets/VisualStudio.targets b/src/roslyn/eng/targets/VisualStudio.targets index 7d7c4a32833..904f9d3410f 100644 --- a/src/roslyn/eng/targets/VisualStudio.targets +++ b/src/roslyn/eng/targets/VisualStudio.targets @@ -142,7 +142,7 @@ Condition="$([MSBuild]::ValueOrDefault('$(ExtensionsPath)', '').Contains('16.0'))"/> - + diff --git a/src/roslyn/eng/test-build-correctness.ps1 b/src/roslyn/eng/test-build-correctness.ps1 index 073b80f8d34..fffe4dedcff 100644 --- a/src/roslyn/eng/test-build-correctness.ps1 +++ b/src/roslyn/eng/test-build-correctness.ps1 @@ -71,8 +71,7 @@ try { # Verify the state of our generated syntax files Write-Host "Checking generated compiler files" - & eng/generate-compiler-code.ps1 -test -configuration:$configuration - Test-LastExitCode + Exec-DotNet "run --file eng/generate-compiler-code.cs -- -test -configuration $configuration" Exec-DotNet "tool run dotnet-format whitespace . --folder --include-generated --include src/Compilers/CSharp/Portable/Generated/ src/Compilers/VisualBasic/Portable/Generated/ src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Generated/ --verify-no-changes" Write-Host "" diff --git a/src/roslyn/global.json b/src/roslyn/global.json index ed08b2111bd..e8cf0f3b286 100644 --- a/src/roslyn/global.json +++ b/src/roslyn/global.json @@ -12,8 +12,8 @@ "vswhere": "3.1.7" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25427.2", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25427.2", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.25469.3", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.25469.3", "Microsoft.Build.Traversal": "3.4.0" } } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.Analyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.Analyzer.cs index 81a4794e13a..29b8435efca 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.Analyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.Analyzer.cs @@ -162,8 +162,7 @@ private bool CanConvertLabelsToArms(SyntaxList labels) // Then we can't convert into a single arm. foreach (var label in labels) { - if (label is CasePatternSwitchLabelSyntax casePattern && - casePattern.WhenClause != null) + if (label is CasePatternSwitchLabelSyntax { WhenClause: not null }) { return false; } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs index f8a82406481..6600f869dac 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs @@ -31,8 +31,8 @@ protected override bool IsValidTypeofAction(OperationAnalysisContext context) // Make sure that the syntax that we're looking at is actually a typeof expression and that // the parent syntax is a member access expression otherwise the syntax is not the kind of // expression that we want to analyze - return node is TypeOfExpressionSyntax { Parent: MemberAccessExpressionSyntax } typeofExpression && - // nameof(System.Void) isn't allowed in C#. - typeofExpression is not { Type: PredefinedTypeSyntax { Keyword.RawKind: (int)SyntaxKind.VoidKeyword } }; + return node is TypeOfExpressionSyntax { Parent: MemberAccessExpressionSyntax } + // nameof(System.Void) isn't allowed in C#. + and not { Type: PredefinedTypeSyntax { Keyword.RawKind: (int)SyntaxKind.VoidKeyword } }; } } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs index f0c45c29ded..eb538fea66e 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs @@ -104,7 +104,7 @@ void ProcessResults( // No need to lock the dictionary here. Processing only is called once, after all mutation work is done. foreach (var (method, diagnostic) in methodToDiagnostic) { - if (method.IsInitOnly && method.AssociatedSymbol is IPropertySymbol owningProperty) + if (method is { IsInitOnly: true, AssociatedSymbol: IPropertySymbol owningProperty }) { // Iff we have an init method that we want to mark as readonly, we can only do so if there is no // `get` accessor, or if the `get` method is already `readonly` or would determined we want to @@ -181,8 +181,7 @@ private static (Location? location, Location? additionalLocation) GetDiagnosticL // An init accessor in a readonly property is already readonly. No need to analyze it. Note: there is no way // to tell this symbolically. We have to check to the syntax here. - if (owningMethod.IsInitOnly && - owningMethod.AssociatedSymbol is IPropertySymbol { DeclaringSyntaxReferences: [var reference, ..] } && + if (owningMethod is { IsInitOnly: true, AssociatedSymbol: IPropertySymbol { DeclaringSyntaxReferences: [var reference, ..] } } && reference.GetSyntax(cancellationToken) is PropertyDeclarationSyntax property && property.Modifiers.Any(SyntaxKind.ReadOnlyKeyword)) { diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryDiscardDesignation/CSharpRemoveUnnecessaryDiscardDesignationDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryDiscardDesignation/CSharpRemoveUnnecessaryDiscardDesignationDiagnosticAnalyzer.cs index ff48a216f5b..5f16decccd4 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryDiscardDesignation/CSharpRemoveUnnecessaryDiscardDesignationDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryDiscardDesignation/CSharpRemoveUnnecessaryDiscardDesignationDiagnosticAnalyzer.cs @@ -81,8 +81,7 @@ private void AnalyzeDiscardDesignation(SyntaxNodeAnalysisContext context) else if (discard.Parent is RecursivePatternSyntax recursivePattern) { // can't remove from `(int i) _` as `(int i)` is not a legal pattern itself. - if (recursivePattern.PositionalPatternClause != null && - recursivePattern.PositionalPatternClause.Subpatterns.Count == 1) + if (recursivePattern.PositionalPatternClause is { Subpatterns.Count: 1 }) { return; } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs index 73765482364..d31694d8f4d 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryLambdaExpression/CSharpRemoveUnnecessaryLambdaExpressionDiagnosticAnalyzer.cs @@ -132,13 +132,13 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, INamedTypeSymbol? return; // cannot convert a partial-definition to a delegate (unless there's an existing implementation part that can be used). - if (invokedMethod.IsPartialDefinition && invokedMethod.PartialImplementationPart is null) + if (invokedMethod is { IsPartialDefinition: true, PartialImplementationPart: null }) return; // Check if the invoked method is a member of a struct. If it is, and it is not readonly and the invoked method // is not readonly, then we don't want to convert it to a method group. If we did, we may accidently change the // behaviour of the code, since structs are value types and the method group will be copied rather than referenced. - if (invokedMethod.ContainingType.TypeKind == TypeKind.Struct && !invokedMethod.IsReadOnly && !invokedMethod.ContainingType.IsReadOnly) + if (invokedMethod is { ContainingType.TypeKind: TypeKind.Struct, IsReadOnly: false, ContainingType.IsReadOnly: false }) return; // If we're calling a generic method, we have to have supplied type arguments. They cannot be inferred once @@ -307,7 +307,7 @@ public static bool TryGetAnonymousFunctionInvocation( if (anonymousFunction.ExpressionBody != null) return TryGetInvocation(anonymousFunction.ExpressionBody, out invocation, out wasAwaited); - if (anonymousFunction.Block != null && anonymousFunction.Block.Statements.Count == 1) + if (anonymousFunction.Block is { Statements.Count: 1 }) { var statement = anonymousFunction.Block.Statements[0]; if (statement is ReturnStatementSyntax { Expression: { } expression }) diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryParentheses/CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryParentheses/CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer.cs index ffb403611d9..8fee35020db 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryParentheses/CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryParentheses/CSharpRemoveUnnecessaryExpressionParenthesesDiagnosticAnalyzer.cs @@ -77,7 +77,7 @@ public static bool CanRemoveParenthesesHelper( parentExpression = isPatternExpression; break; - case ConstantPatternSyntax constantPattern when constantPattern.Parent is IsPatternExpressionSyntax isPatternExpression: + case ConstantPatternSyntax { Parent: IsPatternExpressionSyntax isPatternExpression }: // on the right side of an 'x is const_pattern' expression parentExpression = isPatternExpression; break; diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index 4a6ff87c465..1faf6c3d638 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -101,7 +101,7 @@ public static bool CanReplaceWithCollectionExpression( return false; // (X[])[1, 2, 3] is target typed. `(X)[1, 2, 3]` is currently not (because it looks like indexing into an expr). - if (topMostExpression.Parent is CastExpressionSyntax castExpression && castExpression.Type is IdentifierNameSyntax) + if (topMostExpression.Parent is CastExpressionSyntax { Type: IdentifierNameSyntax }) return false; // X[] = new Y[] { 1, 2, 3 } @@ -977,13 +977,13 @@ bool IsCompatibleSignatureAndArguments( if (arguments.Count == 1 && compilation.SupportsRuntimeCapability(RuntimeCapability.InlineArrayTypes) && originalCreateMethod.Parameters is [ - { - Type: INamedTypeSymbol { - Name: nameof(Span<>) or nameof(ReadOnlySpan<>), - TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] - } spanType - }]) + Type: INamedTypeSymbol + { + Name: nameof(Span<>) or nameof(ReadOnlySpan<>), + TypeArguments: [ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Method }] + } spanType + }]) { if (spanType.OriginalDefinition.Equals(compilation.SpanOfTType()) || spanType.OriginalDefinition.Equals(compilation.ReadOnlySpanOfTType())) @@ -1021,7 +1021,7 @@ public static bool IsArgumentCompatibleWithIEnumerableOfT( if (argExpression is ObjectCreationExpressionSyntax objectCreation) { // Can't have any arguments, as we cannot preserve them once we grab out all the elements. - if (objectCreation.ArgumentList != null && objectCreation.ArgumentList.Arguments.Count > 0) + if (objectCreation.ArgumentList is { Arguments.Count: > 0 }) return false; // If it's got an initializer, it has to be a collection initializer (or an empty object initializer); diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs index 3d39d9fb163..97f3771cc11 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs @@ -199,8 +199,7 @@ private bool IsReferenceEqualsNullCheck( arg1.Kind() == SyntaxKind.NullLiteralExpression) { var symbol = semanticModel.GetSymbolInfo(invocation, cancellationToken).Symbol; - if (symbol?.Name == nameof(ReferenceEquals) && - symbol.ContainingType?.SpecialType == SpecialType.System_Object) + if (symbol is { Name: nameof(ReferenceEquals), ContainingType.SpecialType: SpecialType.System_Object }) { testedExpression = arg0.Kind() == SyntaxKind.NullLiteralExpression ? arg1 : arg0; } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs index 9f7cd1a256e..9817d759094 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseDeconstruction/CSharpUseDeconstructionDiagnosticAnalyzer.cs @@ -151,10 +151,7 @@ private static bool TryAnalyze( if (!IsViableTupleTypeSyntax(typeNode)) return false; - if (conversion.Exists && - !conversion.IsIdentity && - !conversion.IsTupleConversion && - !conversion.IsTupleLiteralConversion) + if (conversion is { Exists: true, IsIdentity: false, IsTupleConversion: false, IsTupleLiteralConversion: false }) { // If there is any other conversion, we bail out because the source type might not be a tuple // or it is a tuple but only thanks to target type inference, which won't occur in a deconstruction. diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs index 970c92cee9b..a51fa0b1752 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs @@ -57,9 +57,7 @@ protected override bool CreateReturnStatementForExpression( // if it's 'async TaskLike' (where TaskLike is non-generic) we do *not* want to // create a return statement. This is just the 'async' version of a 'void' local function. var symbol = semanticModel.GetDeclaredSymbol(statement); - return symbol is IMethodSymbol methodSymbol && - methodSymbol.ReturnType is INamedTypeSymbol namedType && - namedType.Arity != 0; + return symbol is IMethodSymbol { ReturnType: INamedTypeSymbol { Arity: not 0 } }; } return !statement.ReturnType.IsVoid(); diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index d61f5abe544..94ca346c50d 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -57,7 +57,7 @@ protected override bool CreateReturnStatementForExpression( // if it's 'async TaskLike' (where TaskLike is non-generic) we do *not* want to // create a return statement. This is just the 'async' version of a 'void' method. var method = semanticModel.GetDeclaredSymbol(declaration); - return method.ReturnType is INamedTypeSymbol namedType && namedType.Arity != 0; + return method.ReturnType is INamedTypeSymbol { Arity: not 0 }; } return !declaration.ReturnType.IsVoid(); diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs index a0ea8cc4a4c..fa00a7e87a5 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs @@ -40,7 +40,7 @@ public UseExpressionBodyDiagnosticAnalyzer() } public override DiagnosticAnalyzerCategory GetAnalyzerCategory() - => DiagnosticAnalyzerCategory.SyntaxTreeWithoutSemanticsAnalysis; + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; protected override void InitializeWorker(AnalysisContext context) => context.RegisterSyntaxNodeAction(AnalyzeSyntax, _syntaxKinds); diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs index 4e4f1ae0a87..14f6f5087fb 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; @@ -80,6 +81,7 @@ public static bool Analyze( // 3. Collection-like constructs where the type of the collection is itself explicit. For example: `new // List { new() }` or `new C[] { new() }`. + var isAsync = false; TypeSyntax? typeNode; if (objectCreation.Parent is EqualsValueClauseSyntax @@ -99,15 +101,15 @@ public static bool Analyze( } else if (objectCreation.Parent.IsKind(SyntaxKind.ArrowExpressionClause)) { - typeNode = objectCreation.Parent.Parent switch + (typeNode, isAsync) = objectCreation.Parent.Parent switch { - LocalFunctionStatementSyntax localFunction => localFunction.ReturnType, - MethodDeclarationSyntax method => method.ReturnType, - ConversionOperatorDeclarationSyntax conversion => conversion.Type, - OperatorDeclarationSyntax op => op.ReturnType, - BasePropertyDeclarationSyntax property => property.Type, - AccessorDeclarationSyntax(SyntaxKind.GetAccessorDeclaration) { Parent: AccessorListSyntax { Parent: BasePropertyDeclarationSyntax baseProperty } } => baseProperty.Type, - _ => null, + LocalFunctionStatementSyntax localFunction => (localFunction.ReturnType, localFunction.Modifiers.Any(SyntaxKind.AsyncKeyword)), + MethodDeclarationSyntax method => (method.ReturnType, method.Modifiers.Any(SyntaxKind.AsyncKeyword)), + ConversionOperatorDeclarationSyntax conversion => (conversion.Type, false), + OperatorDeclarationSyntax op => (op.ReturnType, false), + BasePropertyDeclarationSyntax property => (property.Type, false), + AccessorDeclarationSyntax(SyntaxKind.GetAccessorDeclaration) { Parent: AccessorListSyntax { Parent: BasePropertyDeclarationSyntax baseProperty } } => (baseProperty.Type, false), + _ => default, }; } else if (objectCreation.Parent is InitializerExpressionSyntax { Parent: ObjectCreationExpressionSyntax { Type: var collectionType } }) @@ -147,6 +149,18 @@ public static bool Analyze( if (leftType is null || rightType is null) return false; + // In an async context, `Task` and `ValueTask` are considered the same as `T` for purposes of determining + // if the type is apparent. So `new()` is valid for `async Task M() { return new Goo(); }`. + var compilation = semanticModel.Compilation; + if (isAsync) + { + if (leftType.OriginalDefinition.Equals(compilation.TaskOfTType()) || + leftType.OriginalDefinition.Equals(compilation.ValueTaskOfTType())) + { + leftType = leftType.GetTypeArguments().Single(); + } + } + if (leftType.IsErrorType() || rightType.IsErrorType()) return false; diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitOrExplicitType/CSharpTypeStyleDiagnosticAnalyzerBase.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitOrExplicitType/CSharpTypeStyleDiagnosticAnalyzerBase.cs index 2a10d0cd781..acd6f57ac95 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitOrExplicitType/CSharpTypeStyleDiagnosticAnalyzerBase.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseImplicitOrExplicitType/CSharpTypeStyleDiagnosticAnalyzerBase.cs @@ -2,15 +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. +using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle; +internal static class CSharpTypeStyleUtilities +{ + public const string EquivalenceyKey = nameof(EquivalenceyKey); + + public const string BuiltInType = nameof(BuiltInType); + public const string TypeIsApparent = nameof(TypeIsApparent); + public const string Elsewhere = nameof(Elsewhere); +} + internal abstract partial class CSharpTypeStyleDiagnosticAnalyzerBase( string diagnosticId, EnforceOnBuild enforceOnBuild, @@ -21,6 +30,10 @@ internal abstract partial class CSharpTypeStyleDiagnosticAnalyzerBase( [CSharpCodeStyleOptions.VarForBuiltInTypes, CSharpCodeStyleOptions.VarWhenTypeIsApparent, CSharpCodeStyleOptions.VarElsewhere], title, message) { + private static readonly ImmutableDictionary BuiltInTypeProperties = ImmutableDictionary.Empty.Add(CSharpTypeStyleUtilities.EquivalenceyKey, CSharpTypeStyleUtilities.BuiltInType); + private static readonly ImmutableDictionary TypeIsApparentProperties = ImmutableDictionary.Empty.Add(CSharpTypeStyleUtilities.EquivalenceyKey, CSharpTypeStyleUtilities.TypeIsApparent); + private static readonly ImmutableDictionary ElsewhereProperties = ImmutableDictionary.Empty.Add(CSharpTypeStyleUtilities.EquivalenceyKey, CSharpTypeStyleUtilities.Elsewhere); + protected abstract CSharpTypeStyleHelper Helper { get; } public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; @@ -45,16 +58,25 @@ private void HandleVariableDeclaration(SyntaxNodeAnalysisContext context) declaredType, semanticModel, simplifierOptions, cancellationToken); if (!typeStyle.IsStylePreferred || ShouldSkipAnalysis(context, typeStyle.Notification) - || !typeStyle.CanConvert()) + || !typeStyle.CanConvert) { return; } // The severity preference is not Hidden, as indicated by IsStylePreferred. var descriptor = Descriptor; - context.ReportDiagnostic(CreateDiagnostic(descriptor, declarationStatement, declaredType.StripRefIfNeeded().Span, typeStyle.Notification, context.Options)); + context.ReportDiagnostic(DiagnosticHelper.Create( + descriptor, + declarationStatement.SyntaxTree.GetLocation(declaredType.StripRefIfNeeded().Span), + typeStyle.Notification, + context.Options, + additionalLocations: null, + typeStyle.Context switch + { + CSharpTypeStyleHelper.Context.BuiltInType => BuiltInTypeProperties, + CSharpTypeStyleHelper.Context.TypeIsApparent => TypeIsApparentProperties, + CSharpTypeStyleHelper.Context.Elsewhere => ElsewhereProperties, + _ => throw ExceptionUtilities.UnexpectedValue(typeStyle.Context), + })); } - - private static Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, SyntaxNode declaration, TextSpan diagnosticSpan, NotificationOption2 notificationOption, AnalyzerOptions analyzerOptions) - => DiagnosticHelper.Create(descriptor, declaration.SyntaxTree.GetLocation(diagnosticSpan), notificationOption, analyzerOptions, additionalLocations: null, properties: null); } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs index c8d7838f535..e63d84195e4 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs @@ -329,8 +329,7 @@ private Diagnostic CreateDiagnostic(Result result, NotificationOption2 notificat } private static bool IsConstantInt32(IOperation operation, int? value = null) - => operation.ConstantValue.HasValue && - operation.ConstantValue.Value is int i && + => operation.ConstantValue is { HasValue: true, Value: int i } && (value == null || i == value); private static bool IsWriteableIndexer(IInvocationOperation invocation, IPropertySymbol indexer) diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs index 2848ad0d5f3..8f5a044ed66 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs @@ -34,7 +34,7 @@ public static bool IsStringRemoveMethod(IMethodSymbol method) .FirstOrDefault(); public static bool IsPublicInstance(ISymbol symbol) - => !symbol.IsStatic && symbol.DeclaredAccessibility == Accessibility.Public; + => symbol is { IsStatic: false, DeclaredAccessibility: Accessibility.Public }; /// /// Checks if this is `expr.Length` where `expr` is equivalent @@ -42,8 +42,7 @@ public static bool IsPublicInstance(ISymbol symbol) /// of. /// public static bool IsInstanceLengthCheck(IPropertySymbol lengthLikeProperty, IOperation instance, IOperation operation) - => operation is IPropertyReferenceOperation propertyRef && - propertyRef.Instance != null && + => operation is IPropertyReferenceOperation { Instance: not null } propertyRef && lengthLikeProperty.Equals(propertyRef.Property) && CSharpSyntaxFacts.Instance.AreEquivalent(instance.Syntax, propertyRef.Instance.Syntax); @@ -53,8 +52,7 @@ public static bool IsInstanceLengthCheck(IPropertySymbol lengthLikeProperty, IOp /// public static bool IsSubtraction(IOperation operation, [NotNullWhen(true)] out IBinaryOperation? subtraction) { - if (operation is IBinaryOperation binaryOperation && - binaryOperation.OperatorKind == BinaryOperatorKind.Subtract) + if (operation is IBinaryOperation { OperatorKind: BinaryOperatorKind.Subtract } binaryOperation) { subtraction = binaryOperation; return true; diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseIsNullCheckForCastAndEqualityOperatorDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseIsNullCheckForCastAndEqualityOperatorDiagnosticAnalyzer.cs index 8879e612951..33cbdd42558 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseIsNullCheckForCastAndEqualityOperatorDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseIsNullCheckForCastAndEqualityOperatorDiagnosticAnalyzer.cs @@ -78,13 +78,7 @@ private static bool IsObjectCastAndNullCheck( var expressionType = semanticModel.GetTypeInfo(castExpression.Expression).Type; if (expressionType != null) { - if (expressionType is ITypeParameterSymbol typeParameter && - !typeParameter.HasReferenceTypeConstraint) - { - return false; - } - - return true; + return expressionType is not ITypeParameterSymbol { HasReferenceTypeConstraint: false }; } } } diff --git a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseLocalFunction/CSharpUseLocalFunctionDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseLocalFunction/CSharpUseLocalFunctionDiagnosticAnalyzer.cs index d714d421f3d..76e737075fb 100644 --- a/src/roslyn/src/Analyzers/CSharp/Analyzers/UseLocalFunction/CSharpUseLocalFunctionDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/CSharp/Analyzers/UseLocalFunction/CSharpUseLocalFunctionDiagnosticAnalyzer.cs @@ -277,8 +277,7 @@ private static bool CanReplaceAnonymousWithLocalFunction( } else if (nodeToCheck.Parent is MemberAccessExpressionSyntax memberAccessExpression) { - if (memberAccessExpression.Parent is InvocationExpressionSyntax explicitInvocationExpression && - memberAccessExpression.Name.Identifier.ValueText == WellKnownMemberNames.DelegateInvokeName) + if (memberAccessExpression is { Parent: InvocationExpressionSyntax explicitInvocationExpression, Name.Identifier.ValueText: WellKnownMemberNames.DelegateInvokeName }) { references.Add(explicitInvocationExpression.GetLocation()); } @@ -350,9 +349,7 @@ private static bool CheckForLocalDeclarationAndAssignment( { // Type t = null; // t = - if (anonymousFunction?.Parent is AssignmentExpressionSyntax(SyntaxKind.SimpleAssignmentExpression) assignment && - assignment.Parent is ExpressionStatementSyntax expressionStatement && - expressionStatement.Parent is BlockSyntax block) + if (anonymousFunction?.Parent is AssignmentExpressionSyntax(SyntaxKind.SimpleAssignmentExpression) { Parent: ExpressionStatementSyntax { Parent: BlockSyntax block } expressionStatement } assignment) { if (assignment.Left.IsKind(SyntaxKind.IdentifierName)) { diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs index 35252af110f..1ff979d1bff 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/AddAnonymousTypeMemberName/CSharpAddAnonymousTypeMemberNameCodeFixProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.AddAnonymousTypeMemberName; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.AddAnonymousTypeMemberName; @@ -39,5 +40,5 @@ protected override AnonymousObjectMemberDeclaratorSyntax WithName(AnonymousObjec SyntaxFactory.IdentifierName(name))); protected override IEnumerable GetAnonymousObjectMemberNames(AnonymousObjectCreationExpressionSyntax initializer) - => initializer.Initializers.Where(i => i.NameEquals != null).Select(i => i.NameEquals!.Name.Identifier.ValueText); + => initializer.Initializers.SelectAsArray(i => i.NameEquals != null, i => i.NameEquals!.Name.Identifier.ValueText); } diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/roslyn/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index 968dd83fb7e..6237c865efa 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -75,7 +75,6 @@ - @@ -85,8 +84,6 @@ - - @@ -193,4 +190,4 @@ - \ No newline at end of file + diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs index 0be3c5fc586..9027b048348 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs @@ -98,7 +98,8 @@ public static async Task MakeLocalFunctionStaticAsync( var seenDefaultArgumentValue = currentInvocation.ArgumentList.Arguments.Count < localFunction.ParameterList.Parameters.Count; // Add all the non-this parameters to the end. If there is a 'this' parameter, add it to the start. - var newArguments = parameterAndCapturedSymbols.Where(p => !p.symbol.IsThisParameter()).Select( + var newArguments = parameterAndCapturedSymbols.SelectAsArray( + p => !p.symbol.IsThisParameter(), symbolAndCapture => (ArgumentSyntax)generator.Argument( seenNamedArgument || seenDefaultArgumentValue ? symbolAndCapture.symbol.Name : null, symbolAndCapture.symbol.RefKind, diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs deleted file mode 100644 index de80287f51a..00000000000 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.MakeMethodSynchronous; -using Microsoft.CodeAnalysis.Shared.Extensions; - -namespace Microsoft.CodeAnalysis.CSharp.MakeMethodSynchronous; - -using static CSharpSyntaxTokens; - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.MakeMethodSynchronous), Shared] -[ExtensionOrder(After = PredefinedCodeFixProviderNames.AddImport)] -[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 CSharpMakeMethodSynchronousCodeFixProvider() : AbstractMakeMethodSynchronousCodeFixProvider -{ - private const string CS1998 = nameof(CS1998); // This async method lacks 'await' operators and will run synchronously. - - public override ImmutableArray FixableDiagnosticIds { get; } = [CS1998]; - - protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node) - => node.IsAsyncSupportingFunctionSyntax(); - - protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbol, SyntaxNode node, KnownTaskTypes knownTypes) - { - switch (node) - { - case MethodDeclarationSyntax method: return FixMethod(methodSymbol, method, knownTypes); - case LocalFunctionStatementSyntax localFunction: return FixLocalFunction(methodSymbol, localFunction, knownTypes); - case AnonymousMethodExpressionSyntax method: return RemoveAsyncModifierHelpers.WithoutAsyncModifier(method); - case ParenthesizedLambdaExpressionSyntax lambda: return RemoveAsyncModifierHelpers.WithoutAsyncModifier(lambda); - case SimpleLambdaExpressionSyntax lambda: return RemoveAsyncModifierHelpers.WithoutAsyncModifier(lambda); - default: return node; - } - } - private static SyntaxNode FixMethod(IMethodSymbol methodSymbol, MethodDeclarationSyntax method, KnownTaskTypes knownTypes) - { - var newReturnType = FixMethodReturnType(methodSymbol, method.ReturnType, knownTypes); - return RemoveAsyncModifierHelpers.WithoutAsyncModifier(method, newReturnType); - } - - private static SyntaxNode FixLocalFunction(IMethodSymbol methodSymbol, LocalFunctionStatementSyntax localFunction, KnownTaskTypes knownTypes) - { - var newReturnType = FixMethodReturnType(methodSymbol, localFunction.ReturnType, knownTypes); - return RemoveAsyncModifierHelpers.WithoutAsyncModifier(localFunction, newReturnType); - } - - private static TypeSyntax FixMethodReturnType(IMethodSymbol methodSymbol, TypeSyntax returnTypeSyntax, KnownTaskTypes knownTypes) - { - var newReturnType = returnTypeSyntax; - - var returnType = methodSymbol.ReturnType; - if (returnType.OriginalDefinition.Equals(knownTypes.TaskType)) - { - // If the return type is Task, then make the new return type "void". - newReturnType = SyntaxFactory.PredefinedType(VoidKeyword).WithTriviaFrom(returnTypeSyntax); - } - else if (returnType.OriginalDefinition.Equals(knownTypes.TaskOfTType)) - { - // If the return type is Task, then make the new return type "T". - newReturnType = returnType.GetTypeArguments()[0].GenerateTypeSyntax().WithTriviaFrom(returnTypeSyntax); - } - else if (returnType.OriginalDefinition.Equals(knownTypes.IAsyncEnumerableOfTType) && - knownTypes.IEnumerableOfTType != null) - { - // If the return type is IAsyncEnumerable, then make the new return type IEnumerable. - newReturnType = knownTypes.IEnumerableOfTType.Construct(methodSymbol.ReturnType.GetTypeArguments()[0]).GenerateTypeSyntax(); - } - else if (returnType.OriginalDefinition.Equals(knownTypes.IAsyncEnumeratorOfTType) && - knownTypes.IEnumeratorOfTType != null) - { - // If the return type is IAsyncEnumerator, then make the new return type IEnumerator. - newReturnType = knownTypes.IEnumeratorOfTType.Construct(methodSymbol.ReturnType.GetTypeArguments()[0]).GenerateTypeSyntax(); - } - - return newReturnType; - } -} diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs deleted file mode 100644 index 9c5c3e27552..00000000000 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Composition; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.RemoveAsyncModifier; - -namespace Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; - -using static CSharpSyntaxTokens; - -[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.RemoveAsyncModifier), Shared] -[ExtensionOrder(After = PredefinedCodeFixProviderNames.MakeMethodSynchronous)] -[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 partial class CSharpRemoveAsyncModifierCodeFixProvider() : AbstractRemoveAsyncModifierCodeFixProvider -{ - private const string CS1998 = nameof(CS1998); // This async method lacks 'await' operators and will run synchronously. - - public override ImmutableArray FixableDiagnosticIds { get; } = [CS1998]; - - protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node) - => node.IsAsyncSupportingFunctionSyntax(); - - protected override SyntaxNode? ConvertToBlockBody(SyntaxNode node, ExpressionSyntax expressionBody) - { - var semicolonToken = SemicolonToken; - if (expressionBody.TryConvertToStatement(semicolonToken, createReturnStatementForExpression: false, out var statement)) - { - var block = SyntaxFactory.Block(statement); - return node switch - { - MethodDeclarationSyntax method => method.WithBody(block).WithExpressionBody(null).WithSemicolonToken(default), - LocalFunctionStatementSyntax localFunction => localFunction.WithBody(block).WithExpressionBody(null).WithSemicolonToken(default), - AnonymousFunctionExpressionSyntax anonymousFunction => anonymousFunction.WithBody(block).WithExpressionBody(null), - _ => throw ExceptionUtilities.Unreachable() - }; - } - - return null; - } - - protected override SyntaxNode RemoveAsyncModifier(SyntaxGenerator generator, SyntaxNode methodLikeNode) - => methodLikeNode switch - { - MethodDeclarationSyntax method => RemoveAsyncModifierHelpers.WithoutAsyncModifier(method, method.ReturnType), - LocalFunctionStatementSyntax localFunction => RemoveAsyncModifierHelpers.WithoutAsyncModifier(localFunction, localFunction.ReturnType), - AnonymousMethodExpressionSyntax method => AnnotateBlock(generator, RemoveAsyncModifierHelpers.WithoutAsyncModifier(method)), - ParenthesizedLambdaExpressionSyntax lambda => AnnotateBlock(generator, RemoveAsyncModifierHelpers.WithoutAsyncModifier(lambda)), - SimpleLambdaExpressionSyntax lambda => AnnotateBlock(generator, RemoveAsyncModifierHelpers.WithoutAsyncModifier(lambda)), - _ => methodLikeNode, - }; - - // Block bodied lambdas and anonymous methods need to be formatted after changing their modifiers, or their indentation is broken - private static SyntaxNode AnnotateBlock(SyntaxGenerator generator, SyntaxNode node) - => generator.GetExpression(node) == null - ? node.WithAdditionalAnnotations(Formatter.Annotation) - : node; -} diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/RemoveAsyncModifierHelpers.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/RemoveAsyncModifierHelpers.cs deleted file mode 100644 index 1df3eefea5e..00000000000 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/RemoveAsyncModifierHelpers.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Shared.Extensions; - -namespace Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; - -internal static class RemoveAsyncModifierHelpers -{ - internal static SyntaxNode WithoutAsyncModifier(MethodDeclarationSyntax method, TypeSyntax returnType) - { - var newModifiers = RemoveAsyncModifier(method.Modifiers, ref returnType); - return method.WithReturnType(returnType).WithModifiers(newModifiers); - } - - internal static SyntaxNode WithoutAsyncModifier(LocalFunctionStatementSyntax localFunction, TypeSyntax returnType) - { - var newModifiers = RemoveAsyncModifier(localFunction.Modifiers, ref returnType); - return localFunction.WithReturnType(returnType).WithModifiers(newModifiers); - } - - internal static SyntaxNode WithoutAsyncModifier(ParenthesizedLambdaExpressionSyntax lambda) - => lambda.WithAsyncKeyword(default).WithPrependedLeadingTrivia(lambda.AsyncKeyword.LeadingTrivia); - - internal static SyntaxNode WithoutAsyncModifier(SimpleLambdaExpressionSyntax lambda) - => lambda.WithAsyncKeyword(default).WithPrependedLeadingTrivia(lambda.AsyncKeyword.LeadingTrivia); - - internal static SyntaxNode WithoutAsyncModifier(AnonymousMethodExpressionSyntax method) - => method.WithAsyncKeyword(default).WithPrependedLeadingTrivia(method.AsyncKeyword.LeadingTrivia); - - private static SyntaxTokenList RemoveAsyncModifier(SyntaxTokenList modifiers, ref TypeSyntax newReturnType) - { - var asyncTokenIndex = modifiers.IndexOf(SyntaxKind.AsyncKeyword); - SyntaxTokenList newModifiers; - if (asyncTokenIndex == 0) - { - // Have to move the trivia on the async token appropriately. - var asyncLeadingTrivia = modifiers[0].LeadingTrivia; - - if (modifiers.Count > 1) - { - // Move the trivia to the next modifier; - newModifiers = modifiers.Replace( - modifiers[1], - modifiers[1].WithPrependedLeadingTrivia(asyncLeadingTrivia)); - newModifiers = newModifiers.RemoveAt(0); - } - else - { - // move it to the return type. - newModifiers = default; - newReturnType = newReturnType.WithPrependedLeadingTrivia(asyncLeadingTrivia); - } - } - else - { - newModifiers = modifiers.RemoveAt(asyncTokenIndex); - } - - return newModifiers; - } -} diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs index fd0d0c27082..96db15d5bd1 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseExplicitTypeCodeFixProvider.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -35,10 +36,16 @@ public override ImmutableArray FixableDiagnosticIds public override Task RegisterCodeFixesAsync(CodeFixContext context) { - RegisterCodeFix(context, CSharpAnalyzersResources.Use_explicit_type_instead_of_var, nameof(CSharpAnalyzersResources.Use_explicit_type_instead_of_var)); + RegisterCodeFix( + context, + CSharpAnalyzersResources.Use_explicit_type_instead_of_var, + context.Diagnostics.First().Properties[CSharpTypeStyleUtilities.EquivalenceyKey]!); return Task.CompletedTask; } + protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic, Document document, string? equivalenceKey, CancellationToken cancellationToken) + => diagnostic.Properties[CSharpTypeStyleUtilities.EquivalenceyKey] == equivalenceKey; + protected override async Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) @@ -95,7 +102,7 @@ private static async Task HandleDeclarationExpressionAsync(Document document, Sy var tupleTypeSymbol = GetConvertedType(semanticModel, typeSyntax.Parent, cancellationToken); var leadingTrivia = declarationExpression.GetLeadingTrivia() - .Concat(variableDesignation.GetAllPrecedingTriviaToPreviousToken().Where(t => !t.IsWhitespace()).Select(t => t.WithoutAnnotations(SyntaxAnnotation.ElasticAnnotation))); + .Concat(variableDesignation.GetAllPrecedingTriviaToPreviousToken().SelectAsArray(t => !t.IsWhitespace(), t => t.WithoutAnnotations(SyntaxAnnotation.ElasticAnnotation))); var tupleDeclaration = GenerateTupleDeclaration( semanticModel, tupleTypeSymbol, variableDesignation, cancellationToken).WithLeadingTrivia(leadingTrivia); diff --git a/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseImplicitTypeCodeFixProvider.cs b/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseImplicitTypeCodeFixProvider.cs index 9e7e7aefb59..8de161fe7de 100644 --- a/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseImplicitTypeCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/CSharp/CodeFixes/UseImplicitOrExplicitType/UseImplicitTypeCodeFixProvider.cs @@ -5,9 +5,11 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -25,10 +27,16 @@ public override ImmutableArray FixableDiagnosticIds public override Task RegisterCodeFixesAsync(CodeFixContext context) { - RegisterCodeFix(context, CSharpAnalyzersResources.use_var_instead_of_explicit_type, nameof(CSharpAnalyzersResources.use_var_instead_of_explicit_type)); + RegisterCodeFix( + context, + CSharpAnalyzersResources.use_var_instead_of_explicit_type, + context.Diagnostics.First().Properties[CSharpTypeStyleUtilities.EquivalenceyKey]!); return Task.CompletedTask; } + protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic, Document document, string? equivalenceKey, CancellationToken cancellationToken) + => diagnostic.Properties[CSharpTypeStyleUtilities.EquivalenceyKey] == equivalenceKey; + protected override Task FixAllAsync( Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems b/src/roslyn/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems index bd9e6cf7e65..ea632d2d4b2 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems +++ b/src/roslyn/src/Analyzers/CSharp/Tests/CSharpAnalyzers.UnitTests.projitems @@ -50,8 +50,6 @@ - - @@ -66,7 +64,6 @@ - @@ -172,7 +169,7 @@ - + @@ -205,4 +202,4 @@ - \ No newline at end of file + diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs index 5ddc54f833a..2a4674e2160 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/GenerateConstructor/GenerateConstructorTests.cs @@ -25,7 +25,7 @@ public sealed class GenerateConstructorTests(ITestOutputHelper logger) internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (null, new GenerateConstructorCodeFixProvider()); - private readonly NamingStylesTestOptionSets options = new NamingStylesTestOptionSets(LanguageNames.CSharp); + private readonly NamingStylesTestOptionSets options = new(LanguageNames.CSharp); [Fact] public Task TestWithSimpleArgument() diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs b/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs index 37a1d66bd08..dd8c167d88a 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs @@ -24,7 +24,7 @@ internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProvide => (null, new CSharpImplementAbstractClassCodeFixProvider()); private OptionsCollection AllOptionsOff - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/InlineDeclaration/CSharpInlineDeclarationTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/InlineDeclaration/CSharpInlineDeclarationTests.cs index b6b375a5ded..eed2e944d8b 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/InlineDeclaration/CSharpInlineDeclarationTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/InlineDeclaration/CSharpInlineDeclarationTests.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.InlineDeclaration; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitType; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitOrExplicitType; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs deleted file mode 100644 index ac66ce324df..00000000000 --- a/src/roslyn/src/Analyzers/CSharp/Tests/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs +++ /dev/null @@ -1,1522 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.MakeMethodAsynchronous; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeMethodAsynchronous; - -[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] -public sealed partial class MakeMethodAsynchronousTests(ITestOutputHelper logger) - : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) -{ - internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (null, new CSharpMakeMethodAsynchronousCodeFixProvider()); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AwaitInVoidMethodWithModifiers() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - public static void Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - public static async void Test() - { - await Task.Delay(1); - } - } - """, index: 1); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26312")] - public async Task AwaitInTaskMainMethodWithModifiers() - { - var initial = - """ - using System; - using System.Threading.Tasks; - - class Program - { - public static void Main() - { - [|await Task.Delay(1);|] - } - } - """; - await TestAsync(initial, """ - using System; - using System.Threading.Tasks; - - class Program - { - public static async Task Main() - { - await Task.Delay(1); - } - } - """, new(parseOptions: CSharpParseOptions.Default, - compilationOptions: new CSharpCompilationOptions(OutputKind.ConsoleApplication))); - - // no option offered to keep void - await TestActionCountAsync(initial, count: 1, new TestParameters(compilationOptions: new CSharpCompilationOptions(OutputKind.ConsoleApplication))); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26312")] - [WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AwaitInVoidMainMethodWithModifiers_NotEntryPoint() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - public void Main() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - public async void Main() - { - await Task.Delay(1); - } - } - """, index: 1); - - [Fact] - public Task AwaitInVoidMethodWithModifiers2() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - public static void Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - public static async Task TestAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AwaitInTaskMethodNoModifiers() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - Task Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - async Task Test() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AwaitInTaskMethodWithModifiers() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - public/*Comment*/static/*Comment*/Task/*Comment*/Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - public/*Comment*/static/*Comment*/async Task/*Comment*/Test() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public Task AwaitInLambdaFunction() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - static void Main(string[] args) - { - Action a = () => Console.WriteLine(); - Func b = () => [|await Task.Run(a);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - static void Main(string[] args) - { - Action a = () => Console.WriteLine(); - Func b = async () => await Task.Run(a); - } - } - """); - - [Fact] - public Task AwaitInLambdaAction() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - class Program - { - static void Main(string[] args) - { - Action a = () => [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - class Program - { - static void Main(string[] args) - { - Action a = async () => await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task BadAwaitInNonAsyncMethod() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - class Program - { - void Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - class Program - { - async void Test() - { - await Task.Delay(1); - } - } - """, index: 1); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task BadAwaitInNonAsyncMethod2() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - class Program - { - Task Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - class Program - { - async Task Test() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task BadAwaitInNonAsyncMethod3() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - class Program - { - Task Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - class Program - { - async Task Test() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public Task BadAwaitInNonAsyncMethod4() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - class Program - { - int Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - class Program - { - async Task TestAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task BadAwaitInNonAsyncMethod5() - => TestInRegularAndScriptAsync(""" - class Program - { - void Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - class Program - { - async void Test() - { - await Task.Delay(1); - } - } - """, index: 1); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task BadAwaitInNonAsyncMethod6() - => TestInRegularAndScriptAsync(""" - class Program - { - Task Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - class Program - { - async Task Test() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task BadAwaitInNonAsyncMethod7() - => TestInRegularAndScriptAsync(""" - class Program - { - Task Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - class Program - { - async Task Test() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public Task BadAwaitInNonAsyncMethod8() - => TestInRegularAndScriptAsync(""" - class Program - { - int Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - - class Program - { - async Task TestAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public Task BadAwaitInNonAsyncMethod9() - => TestInRegularAndScriptAsync(""" - class Program - { - Program Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - - class Program - { - async Task TestAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public Task BadAwaitInNonAsyncMethod10() - => TestInRegularAndScriptAsync(""" - class Program - { - asdf Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - class Program - { - async System.Threading.Tasks.Task TestAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public async Task BadAwaitInEnumerableMethod() - { - var initial = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerable Test() - { - yield return 1; - [|await Task.Delay(1);|] - } - } - """ + IAsyncEnumerable; - - var expected = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerable TestAsync() - { - yield return 1; - await Task.Delay(1); - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact] - public Task BadAwaitInEnumerableMethodMissingIAsyncEnumerableType() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerable Test() - { - yield return 1; - [|await Task.Delay(1);|] - } - } - """, """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerable TestAsync() - { - yield return 1; - await Task.Delay(1); - } - } - """); - - [Fact] - public Task BadAwaitInEnumerableMethodWithReturn() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerable Test() - { - [|await Task.Delay(1);|] - return null; - } - } - """, """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async Task> TestAsync() - { - await Task.Delay(1); - return null; - } - } - """); - - [Fact] - public Task BadAwaitInEnumerableMethodWithYieldInsideLocalFunction() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerable Test() - { - [|await Task.Delay(1);|] - return local(); - - IEnumerable local() - { - yield return 1; - } - } - } - """, """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async Task> TestAsync() - { - await Task.Delay(1); - return local(); - - IEnumerable local() - { - yield return 1; - } - } - } - """); - - [Fact] - public Task BadAwaitInEnumeratorMethodWithReturn() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerator Test() - { - [|await Task.Delay(1);|] - return null; - } - } - """, """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async Task> TestAsync() - { - await Task.Delay(1); - return null; - } - } - """); - - [Fact] - public async Task BadAwaitInEnumeratorMethod() - { - var initial = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerator Test() - { - yield return 1; - [|await Task.Delay(1);|] - } - } - """ + IAsyncEnumerable; - - var expected = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerator TestAsync() - { - yield return 1; - await Task.Delay(1); - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact] - public async Task BadAwaitInEnumeratorLocalFunction() - { - var initial = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - void M() - { - IEnumerator Test() - { - yield return 1; - [|await Task.Delay(1);|] - } - } - } - """ + IAsyncEnumerable; - - var expected = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - void M() - { - async IAsyncEnumerator TestAsync() - { - yield return 1; - await Task.Delay(1); - } - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public async Task BadAwaitInIAsyncEnumerableMethod() - { - var initial = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IAsyncEnumerable Test() - { - yield return 1; - [|await Task.Delay(1);|] - } - } - """ + IAsyncEnumerable; - - var expected = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerable Test() - { - yield return 1; - await Task.Delay(1); - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public async Task BadAwaitInIAsyncEnumeratorMethod() - { - var initial = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IAsyncEnumerator Test() - { - yield return 1; - [|await Task.Delay(1);|] - } - } - """ + IAsyncEnumerable; - - var expected = - """ - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerator Test() - { - yield return 1; - await Task.Delay(1); - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact] - public Task AwaitInMember() - => TestMissingInRegularAndScriptAsync(""" - using System.Threading.Tasks; - - class Program - { - var x = [|await Task.Delay(3)|]; - } - """); - - [Fact] - public Task AddAsyncInDelegate() - => TestInRegularAndScriptAsync( - """ - using System; - using System.Threading.Tasks; - - class Program - { - private async void method() - { - string content = await Task.Run(delegate () { - [|await Task.Delay(1000)|]; - return "Test"; - }); - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class Program - { - private async void method() - { - string content = await Task.Run(async delegate () { - await Task.Delay(1000); - return "Test"; - }); - } - } - """); - - [Fact] - public Task AddAsyncInDelegate2() - => TestInRegularAndScriptAsync( - """ - using System; - using System.Threading.Tasks; - - class Program - { - private void method() - { - string content = await Task.Run(delegate () { - [|await Task.Delay(1000)|]; - return "Test"; - }); - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class Program - { - private void method() - { - string content = await Task.Run(async delegate () { - await Task.Delay(1000); - return "Test"; - }); - } - } - """); - - [Fact] - public Task AddAsyncInDelegate3() - => TestInRegularAndScriptAsync( - """ - using System; - using System.Threading.Tasks; - - class Program - { - private void method() - { - string content = await Task.Run(delegate () { - [|await Task.Delay(1000)|]; - return "Test"; - }); - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class Program - { - private void method() - { - string content = await Task.Run(async delegate () { - await Task.Delay(1000); - return "Test"; - }); - } - } - """); - - [Fact, WorkItem(6477, @"https://github.com/dotnet/roslyn/issues/6477")] - public Task NullNodeCrash() - => TestMissingInRegularAndScriptAsync( - """ - using System.Threading.Tasks; - - class C - { - static async void Main() - { - try - { - [|await|] await Task.Delay(100); - } - finally - { - } - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - [WorkItem("https://github.com/dotnet/roslyn/issues/17470")] - public Task AwaitInValueTaskMethod() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - namespace System.Threading.Tasks { - struct ValueTask - { - } - } - - class Program - { - ValueTask Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - namespace System.Threading.Tasks { - struct ValueTask - { - } - } - - class Program - { - async ValueTask Test() - { - await Task.Delay(1); - } - } - """); - - [Fact] - public Task AwaitInValueTaskWithoutGenericMethod() - => TestInRegularAndScriptAsync(""" - using System; - using System.Threading.Tasks; - - namespace System.Threading.Tasks { - struct ValueTask - { - } - } - - class Program - { - ValueTask Test() - { - [|await Task.Delay(1);|] - } - } - """, """ - using System; - using System.Threading.Tasks; - - namespace System.Threading.Tasks { - struct ValueTask - { - } - } - - class Program - { - async ValueTask Test() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14133")] - public Task AddAsyncInLocalFunction() - => TestInRegularAndScriptAsync( - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - void M2() - { - [|await M3Async();|] - } - } - - async Task M3Async() - { - return 1; - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - async Task M2Async() - { - await M3Async(); - } - } - - async Task M3Async() - { - return 1; - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14133")] - [WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AddAsyncInLocalFunctionKeepVoidReturn() - => TestInRegularAndScriptAsync( - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - void M2() - { - [|await M3Async();|] - } - } - - async Task M3Async() - { - return 1; - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - async void M2() - { - await M3Async(); - } - } - - async Task M3Async() - { - return 1; - } - } - """, - index: 1); - - [Theory] - [InlineData(0, "void", "Task", "M2Async")] - [InlineData(1, "void", "void", "M2")] - [InlineData(0, "int", "Task", "M2Async")] - [InlineData(0, "Task", "Task", "M2")] - [WorkItem("https://github.com/dotnet/roslyn/issues/18307")] - [WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AddAsyncInLocalFunctionKeepsTrivia(int codeFixIndex, string initialReturn, string expectedReturn, string expectedName) - => TestInRegularAndScriptAsync( - $$""" - using System.Threading.Tasks; - - class C - { - public void M1() - { - // Leading trivia - /*1*/ {{initialReturn}} /*2*/ M2/*3*/() /*4*/ - { - [|await M3Async();|] - } - } - - async Task M3Async() - { - return 1; - } - } - """, - $$""" - using System.Threading.Tasks; - - class C - { - public void M1() - { - // Leading trivia - /*1*/ async {{expectedReturn}} /*2*/ {{expectedName}}/*3*/() /*4*/ - { - await M3Async(); - } - } - - async Task M3Async() - { - return 1; - } - } - """, - index: codeFixIndex); - - [Theory] - [InlineData("", 0, "Task", "M2Async")] - [InlineData("", 1, "void", "M2")] - [InlineData("public", 0, "Task", "M2Async")] - [InlineData("public", 1, "void", "M2")] - [WorkItem("https://github.com/dotnet/roslyn/issues/18307")] - [WorkItem("https://github.com/dotnet/roslyn/issues/33082")] - public Task AddAsyncKeepsTrivia(string modifiers, int codeFixIndex, string expectedReturn, string expectedName) - => TestInRegularAndScriptAsync( - $$""" - using System.Threading.Tasks; - - class C - { - // Leading trivia - {{modifiers}}/*1*/ void /*2*/ M2/*3*/() /*4*/ - { - [|await M3Async();|] - } - - async Task M3Async() - { - return 1; - } - } - """, - $$""" - using System.Threading.Tasks; - - class C - { - // Leading trivia - {{modifiers}}/*1*/ async {{expectedReturn}} /*2*/ {{expectedName}}/*3*/() /*4*/ - { - await M3Async(); - } - - async Task M3Async() - { - return 1; - } - } - """, - index: codeFixIndex); - - [Fact] - public Task MethodWithAwaitUsing() - => TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - [|await using (var x = new object())|] - { - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - async Task MAsync() - { - await using (var x = new object()) - { - } - } - } - """); - - [Fact] - public Task MethodWithRegularUsing() - => TestMissingInRegularAndScriptAsync( - """ - class C - { - void M() - { - [|using (var x = new object())|] - { - } - } - } - """); - - [Fact] - public Task MethodWithAwaitForEach() - => TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - [|await foreach (var n in new int[] { })|] - { - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - async Task MAsync() - { - await foreach (var n in new int[] { }) - { - } - } - } - """); - - [Fact] - public Task MethodWithRegularForEach() - => TestMissingInRegularAndScriptAsync( - """ - class C - { - void M() - { - [|foreach (var n in new int[] { })|] - { - } - } - } - """); - - [Fact] - public Task MethodWithAwaitForEachVariable() - => TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - [|await foreach (var (a, b) in new(int, int)[] { })|] - { - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - async Task MAsync() - { - await foreach (var (a, b) in new(int, int)[] { }) - { - } - } - } - """); - - [Fact] - public Task MethodWithRegularForEachVariable() - => TestMissingInRegularAndScriptAsync( - """ - class C - { - void M() - { - [|foreach (var (a, b) in new(int, int)[] { })|] - { - } - } - } - """); - - [Fact] - public Task MethodWithNullableReturn() - => TestInRegularAndScriptAsync( - """ - #nullable enable - using System.Threading.Tasks; - class C - { - string? M() - { - [|await Task.Delay(1);|] - return null; - } - } - """, - """ - #nullable enable - using System.Threading.Tasks; - class C - { - async Task MAsync() - { - await Task.Delay(1); - return null; - } - } - """); - - [Fact] - public async Task EnumerableMethodWithNullableType() - { - var initial = - """ - #nullable enable - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerable Test() - { - yield return string.Empty; - [|await Task.Delay(1);|] - } - } - """ + IAsyncEnumerable; - - var expected = - """ - #nullable enable - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerable TestAsync() - { - yield return string.Empty; - await Task.Delay(1); - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact] - public async Task EnumeratorMethodWithNullableType() - { - var initial = - """ - #nullable enable - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - IEnumerator Test() - { - yield return string.Empty; - [|await Task.Delay(1);|] - } - } - """ + IAsyncEnumerable; - - var expected = - """ - #nullable enable - using System.Threading.Tasks; - using System.Collections.Generic; - class Program - { - async IAsyncEnumerator TestAsync() - { - yield return string.Empty; - await Task.Delay(1); - } - } - """ + IAsyncEnumerable; - await TestInRegularAndScriptAsync(initial, expected); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25446")] - public Task TestOnAwaitParsedAsType() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - - class C - { - void M() - { - Task task = null; - [|await|] task; - } - } - """, """ - using System.Threading.Tasks; - - class C - { - async Task MAsync() - { - Task task = null; - await task; - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63404")] - public Task PartialMethod1() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - - public partial class C - { - partial void M(); - } - - public partial class C - { - partial void M() - { - [|await|] Task.Delay(1); - } - } - """, """ - using System.Threading.Tasks; - - public partial class C - { - partial Task MAsync(); - } - - public partial class C - { - async partial Task MAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63404")] - public Task PartialMethod2() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - - public partial class C - { - public partial void M(); - } - - public partial class C - { - public partial void M() - { - [|await|] Task.Delay(1); - } - } - """, """ - using System.Threading.Tasks; - - public partial class C - { - public partial Task MAsync(); - } - - public partial class C - { - public async partial Task MAsync() - { - await Task.Delay(1); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63404")] - public Task PartialMethod3() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - - public partial class C - { - partial void M(); - } - - public partial class C - { - partial void M() - { - [|await|] Task.Delay(1); - } - } - """, """ - using System.Threading.Tasks; - - public partial class C - { - partial void M(); - } - - public partial class C - { - async partial void M() - { - await Task.Delay(1); - } - } - """, index: 1); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63404")] - public Task PartialMethod4() - => TestInRegularAndScriptAsync(""" - using System.Threading.Tasks; - - public partial class C - { - public partial void M(); - } - - public partial class C - { - public partial void M() - { - [|await|] Task.Delay(1); - } - } - """, """ - using System.Threading.Tasks; - - public partial class C - { - public partial void M(); - } - - public partial class C - { - public async partial void M() - { - await Task.Delay(1); - } - } - """, index: 1); -} diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs deleted file mode 100644 index 2ab8c1bf6b9..00000000000 --- a/src/roslyn/src/Analyzers/CSharp/Tests/MakeMethodSynchronous/MakeMethodSynchronousTests.cs +++ /dev/null @@ -1,1045 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.MakeMethodSynchronous; -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.MakeMethodSynchronous; - -using VerifyCS = CSharpCodeFixVerifier< - EmptyDiagnosticAnalyzer, - CSharpMakeMethodSynchronousCodeFixProvider>; - -[Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodSynchronous)] -public sealed class MakeMethodSynchronousTests -{ - [Fact] - public Task TestTaskReturnType() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - void Goo() - { - } - } - """); - - [Fact] - public Task TestTaskOfTReturnType() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - return 1; - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - int {|#0:Goo|}() - { - return 1; - } - } - """); - - [Fact] - public Task TestSecondModifier() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - public async Task {|CS1998:Goo|}() - { - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void Goo() - { - } - } - """); - - [Fact] - public Task TestFirstModifier() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async public Task {|CS1998:Goo|}() - { - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void Goo() - { - } - } - """); - - [Fact] - public Task TestTrailingTrivia() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async // comment - Task {|CS1998:Goo|}() - { - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - void Goo() - { - } - } - """); - - [Fact] - public Task TestRenameMethod() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:GooAsync|}() - { - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - void Goo() - { - } - } - """); - - [Fact] - public Task TestRenameMethod1() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:GooAsync|}() - { - } - - void Bar() - { - GooAsync(); - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - void Goo() - { - } - - void Bar() - { - Goo(); - } - } - """); - - [Fact] - public async Task TestParenthesizedLambda() - { - var expected = - """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func f = - () {|#0:=>|} { }; - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func f = - async () {|CS1998:=>|} { }; - } - } - """, - FixedState = - { - Sources = { expected }, - ExpectedDiagnostics = - { - // /0/Test0.cs(10,16): error CS1643: Not all code paths return a value in lambda expression of type 'Func' - DiagnosticResult.CompilerError("CS1643").WithLocation(0), - }, - }, - }.RunAsync(); - } - - [Fact] - public async Task TestSimpleLambda() - { - var expected = - """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func f = - a {|#0:=>|} { }; - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func f = - async a {|CS1998:=>|} { }; - } - } - """, - FixedState = - { - Sources = { expected }, - ExpectedDiagnostics = - { - // /0/Test0.cs(10,15): error CS1643: Not all code paths return a value in lambda expression of type 'Func' - DiagnosticResult.CompilerError("CS1643").WithLocation(0), - }, - }, - }.RunAsync(); - } - - [Fact] - public async Task TestLambdaWithExpressionBody() - { - var expected = - """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func> f = - a => {|#0:1|}; - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func> f = - async a {|CS1998:=>|} 1; - } - } - """, - FixedState = - { - Sources = { expected }, - ExpectedDiagnostics = - { - // /0/Test0.cs(10,18): error CS0029: Cannot implicitly convert type 'int' to 'System.Threading.Tasks.Task' - DiagnosticResult.CompilerError("CS0029").WithLocation(0).WithArguments("int", "System.Threading.Tasks.Task"), - // /0/Test0.cs(10,18): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type - DiagnosticResult.CompilerError("CS1662").WithLocation(0), - }, - }, - }.RunAsync(); - } - - [Fact] - public async Task TestAnonymousMethod() - { - var expected = - """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func f = - {|#0:delegate|} { }; - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System; - using System.Threading.Tasks; - - class C - { - void Goo() - { - Func f = - async {|CS1998:delegate|} { }; - } - } - """, - FixedState = - { - Sources = { expected }, - ExpectedDiagnostics = - { - // /0/Test0.cs(10,13): error CS1643: Not all code paths return a value in anonymous method of type 'Func' - DiagnosticResult.CompilerError("CS1643").WithLocation(0), - }, - }, - }.RunAsync(); - } - - [Fact] - public Task TestFixAll() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}() - { - BarAsync(); - } - - async Task {|#0:{|CS1998:BarAsync|}|}() - { - GooAsync(); - return 1; - } - } - """, - """ - using System.Threading.Tasks; - - public class Class1 - { - void Goo() - { - Bar(); - } - - int {|#0:Bar|}() - { - Goo(); - return 1; - } - } - """); - - [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] - public async Task TestRemoveAwaitFromCaller1() - { - var expected = - """ - using System.Threading.Tasks; - - public class Class1 - { - void Goo() - { - } - - async void {|CS1998:BarAsync|}() - { - Goo(); - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}() - { - } - - async void BarAsync() - { - await GooAsync(); - } - } - """, - FixedState = - { - Sources = { expected }, - MarkupHandling = MarkupMode.Allow, - }, - CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne, - }.RunAsync(); - } - - [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] - public async Task TestRemoveAwaitFromCaller2() - { - var expected = - """ - using System.Threading.Tasks; - - public class Class1 - { - void Goo() - { - } - - async void {|CS1998:BarAsync|}() - { - Goo(); - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}() - { - } - - async void BarAsync() - { - await GooAsync().ConfigureAwait(false); - } - } - """, - FixedState = - { - Sources = { expected }, - MarkupHandling = MarkupMode.Allow, - }, - CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne, - }.RunAsync(); - } - - [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] - public async Task TestRemoveAwaitFromCaller3() - { - var expected = - """ - using System.Threading.Tasks; - - public class Class1 - { - void Goo() - { - } - - async void {|CS1998:BarAsync|}() - { - this.Goo(); - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}() - { - } - - async void BarAsync() - { - await this.GooAsync(); - } - } - """, - FixedState = - { - Sources = { expected }, - MarkupHandling = MarkupMode.Allow, - }, - CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne, - }.RunAsync(); - } - - [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] - public async Task TestRemoveAwaitFromCaller4() - { - var expected = - """ - using System.Threading.Tasks; - - public class Class1 - { - void Goo() - { - } - - async void {|CS1998:BarAsync|}() - { - this.Goo(); - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}() - { - } - - async void BarAsync() - { - await this.GooAsync().ConfigureAwait(false); - } - } - """, - FixedState = - { - Sources = { expected }, - MarkupHandling = MarkupMode.Allow, - }, - CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne, - }.RunAsync(); - } - - [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] - public async Task TestRemoveAwaitFromCallerNested1() - { - var expected = - """ - using System.Threading.Tasks; - - public class Class1 - { - int Goo(int i) - { - return 1; - } - - async void {|CS1998:BarAsync|}() - { - this.Goo(this.Goo(0)); - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}(int i) - { - return 1; - } - - async void BarAsync() - { - await this.GooAsync(await this.GooAsync(0)); - } - } - """, - FixedState = - { - Sources = { expected }, - MarkupHandling = MarkupMode.Allow, - }, - CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne, - }.RunAsync(); - } - - [Fact] - [WorkItem("https://github.com/dotnet/roslyn/issues/13961")] - public async Task TestRemoveAwaitFromCallerNested() - { - var expected = - """ - using System.Threading.Tasks; - - public class Class1 - { - int Goo(int i) - { - return 1; - } - - async void {|CS1998:BarAsync|}() - { - this.Goo(this.Goo(0)); - } - } - """; - - await new VerifyCS.Test - { - TestCode = """ - using System.Threading.Tasks; - - public class Class1 - { - async Task {|CS1998:GooAsync|}(int i) - { - return 1; - } - - async void BarAsync() - { - await this.GooAsync(await this.GooAsync(0).ConfigureAwait(false)).ConfigureAwait(false); - } - } - """, - FixedState = - { - Sources = { expected }, - MarkupHandling = MarkupMode.Allow, - }, - CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne, - }.RunAsync(); - } - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] - [WorkItem("https://github.com/dotnet/roslyn/issues/14133")] - public Task RemoveAsyncInLocalFunction() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - async Task {|CS1998:M2Async|}() - { - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - void M2() - { - } - } - } - """); - - [Theory] - [InlineData("Task", "C")] - [InlineData("Task", "int")] - [InlineData("Task", "void")] - [InlineData("void", "void")] - [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] - [WorkItem("https://github.com/dotnet/roslyn/issues/18307")] - public Task RemoveAsyncInLocalFunctionKeepsTrivia(string asyncReturn, string expectedReturn) - => VerifyCS.VerifyCodeFixAsync( - $$""" - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - // Leading trivia - /*1*/ async {{asyncReturn}} /*2*/ {|CS1998:M2Async|}/*3*/() /*4*/ - { - throw new NotImplementedException(); - } - } - } - """, - $$""" - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - // Leading trivia - /*1*/ - {{expectedReturn}} /*2*/ M2/*3*/() /*4*/ - { - throw new NotImplementedException(); - } - } - } - """); - - [Theory] - [InlineData("", "Task", "\r\n C")] - [InlineData("", "Task", "\r\n int")] - [InlineData("", "Task", "\r\n void")] - [InlineData("", "void", "\r\n void")] - [InlineData("public", "Task", " C")] - [InlineData("public", "Task", " int")] - [InlineData("public", "Task", " void")] - [InlineData("public", "void", " void")] - [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] - [WorkItem("https://github.com/dotnet/roslyn/issues/18307")] - public Task RemoveAsyncKeepsTrivia(string modifiers, string asyncReturn, string expectedReturn) - => VerifyCS.VerifyCodeFixAsync( - $$""" - using System; - using System.Threading.Tasks; - - class C - { - // Leading trivia - {{modifiers}}/*1*/ async {{asyncReturn}} /*2*/ {|CS1998:M2Async|}/*3*/() /*4*/ - { - throw new NotImplementedException(); - } - } - """, - $$""" - using System; - using System.Threading.Tasks; - - class C - { - // Leading trivia - {{modifiers}}/*1*/{{expectedReturn}} /*2*/ M2/*3*/() /*4*/ - { - throw new NotImplementedException(); - } - } - """); - - [Fact] - public async Task MethodWithUsingAwait() - { - var source = - """ - class C - { - async System.Threading.Tasks.Task MAsync() - { - await using ({|#0:var x = new object()|}) - { - } - } - } - """; - - await new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = source, - ExpectedDiagnostics = - { - // /0/Test0.cs(5,22): error CS8410: 'object': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. - DiagnosticResult.CompilerError("CS8410").WithLocation(0).WithArguments("object"), - }, - FixedCode = source, - }.RunAsync(); - } - - [Fact] - public Task MethodWithUsingNoAwait() - => VerifyCS.VerifyCodeFixAsync( - """ - class C - { - async System.Threading.Tasks.Task {|CS1998:MAsync|}() - { - using ({|#0:var x = new object()|}) - { - } - } - } - """, - // /0/Test0.cs(5,16): error CS1674: 'object': type used in a using statement must implement 'System.IDisposable'. - DiagnosticResult.CompilerError("CS1674").WithLocation(0).WithArguments("object"), - """ - class C - { - void M() - { - using ({|#0:var x = new object()|}) - { - } - } - } - """); - - [Fact] - public async Task MethodWithAwaitForEach() - { - var source = - """ - class C - { - async System.Threading.Tasks.Task MAsync() - { - await foreach (var n in {|#0:new int[] { }|}) - { - } - } - } - """; - - await VerifyCS.VerifyCodeFixAsync( - source, - // /0/Test0.cs(5,33): error CS1061: 'bool' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'bool' could be found (are you missing a using directive or an assembly reference?) - DiagnosticResult.CompilerError("CS1061").WithLocation(0).WithArguments("bool", "GetAwaiter"), - source); - } - - [Fact] - public Task MethodWithForEachNoAwait() - => VerifyCS.VerifyCodeFixAsync( - """ - class C - { - async System.Threading.Tasks.Task {|CS1998:MAsync|}() - { - foreach (var n in new int[] { }) - { - } - } - } - """, - """ - class C - { - void M() - { - foreach (var n in new int[] { }) - { - } - } - } - """); - - [Fact] - public async Task MethodWithForEachVariableAwait() - { - var source = - """ - class C - { - async System.Threading.Tasks.Task MAsync() - { - await foreach (var (a, b) in {|#0:new(int, int)[] { }|}) - { - } - } - } - """; - - await VerifyCS.VerifyCodeFixAsync( - source, - // /0/Test0.cs(5,38): error CS1061: 'bool' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'bool' could be found (are you missing a using directive or an assembly reference?) - DiagnosticResult.CompilerError("CS1061").WithLocation(0).WithArguments("bool", "GetAwaiter"), - source); - } - - [Fact] - public Task MethodWithForEachVariableNoAwait() - => VerifyCS.VerifyCodeFixAsync( - """ - class C - { - async System.Threading.Tasks.Task {|CS1998:MAsync|}() - { - foreach (var (a, b) in new(int, int)[] { }) - { - } - } - } - """, - """ - class C - { - void M() - { - foreach (var (a, b) in new (int, int)[] { }) - { - } - } - } - """); - - [Fact] - public Task TestIAsyncEnumerableReturnType() - => new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = """ - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - async IAsyncEnumerable {|CS1998:MAsync|}() - { - yield return 1; - } - } - """, - FixedCode = """ - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - IEnumerable M() - { - yield return 1; - } - } - """, - }.RunAsync(); - - [Fact] - public Task TestIAsyncEnumeratorReturnTypeOnLocalFunction() - => new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = """ - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - void Method() - { - async IAsyncEnumerator {|CS1998:MAsync|}() - { - yield return 1; - } - } - } - """, - FixedCode = """ - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - void Method() - { - IEnumerator M() - { - yield return 1; - } - } - } - """, - }.RunAsync(); -} diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index a93373e0cdc..f65576cce4a 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -21,7 +21,7 @@ public sealed class CSharpDeclareAsNullableCodeFixTests(ITestOutputHelper logger internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (null, new CSharpDeclareAsNullableCodeFixProvider()); - private static readonly TestParameters s_nullableFeature = new TestParameters(parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)); + private static readonly TestParameters s_nullableFeature = new(parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8)); [Fact] public Task FixAll() diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs deleted file mode 100644 index 59c4359e893..00000000000 --- a/src/roslyn/src/Analyzers/CSharp/Tests/RemoveAsyncModifier/RemoveAsyncModifierTests.cs +++ /dev/null @@ -1,1224 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; -using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -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.RemoveAsyncModifier; - -using VerifyCS = CSharpCodeFixVerifier< - EmptyDiagnosticAnalyzer, - CSharpRemoveAsyncModifierCodeFixProvider>; - -[Trait(Traits.Feature, Traits.Features.CodeActionsRemoveAsyncModifier)] -public sealed class RemoveAsyncModifierTests -{ - [Fact] - public Task Method_Task_MultipleAndNested() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - if (DateTime.Now.Ticks > 0) - { - return; - } - } - - async Task {|CS1998:Foo|}() - { - Console.WriteLine(1); - } - - async Task {|CS1998:Bar|}() - { - async Task {|CS1998:Baz|}() - { - Func> g = async () {|CS1998:=>|} 5; - } - } - - async Task {|CS1998:Tur|}() - { - async Task {|CS1998:Duck|}() - { - async Task {|CS1998:En|}() - { - return "Developers!"; - } - - return "Developers! Developers!"; - } - - return "Developers! Developers! Developers!"; - } - - async Task {|CS1998:Nurk|}() - { - Func> f = async () {|CS1998:=>|} 4; - - if (DateTime.Now.Ticks > f().Result) - { - } - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - Task Goo() - { - if (DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - } - - Task Foo() - { - Console.WriteLine(1); - return Task.CompletedTask; - } - - Task Bar() - { - Task Baz() - { - Func> g = () => Task.FromResult(5); - return Task.CompletedTask; - } - - return Task.CompletedTask; - } - - Task Tur() - { - Task Duck() - { - Task En() - { - return Task.FromResult("Developers!"); - } - - return Task.FromResult("Developers! Developers!"); - } - - return Task.FromResult("Developers! Developers! Developers!"); - } - - Task Nurk() - { - Func> f = () => Task.FromResult(4); - - if (DateTime.Now.Ticks > f().Result) - { - } - - return Task.CompletedTask; - } - } - """); - - [Fact] - public Task Method_Task_EmptyBlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}(){} - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() - { - return Task.CompletedTask; - } - } - """); - - [Fact] - public Task Method_Task_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - } - } - """); - - [Fact] - public Task Method_ValueTask_BlockBody() - => new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = """ - using System.Threading.Tasks; - - class C - { - async ValueTask {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - } - } - """, - FixedCode = """ - using System.Threading.Tasks; - - class C - { - ValueTask Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return new ValueTask(); - } - - return new ValueTask(); - } - } - """, - }.RunAsync(); - - [Fact] - public Task Method_ValueTaskOfT_BlockBody() - => new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = """ - using System.Threading.Tasks; - - class C - { - async ValueTask {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return 2; - } - - return 3; - } - } - """, - FixedCode = """ - using System.Threading.Tasks; - - class C - { - ValueTask Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return new ValueTask(2); - } - - return new ValueTask(3); - } - } - """, - }.RunAsync(); - - [Fact] - public Task Method_ValueTask_ExpressionBody() - => new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = """ - using System.Threading.Tasks; - - class C - { - async ValueTask {|CS1998:Goo|}() => System.Console.WriteLine(1); - } - """, - FixedCode = """ - using System.Threading.Tasks; - - class C - { - ValueTask Goo() - { - System.Console.WriteLine(1); - return new ValueTask(); - } - } - """, - }.RunAsync(); - - [Fact] - public Task Method_ValueTaskOfT_ExpressionBody() - => new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = """ - using System.Threading.Tasks; - - class C - { - async ValueTask {|CS1998:Goo|}() => 3; - } - """, - FixedCode = """ - using System.Threading.Tasks; - - class C - { - ValueTask Goo() => new ValueTask(3); - } - """, - }.RunAsync(); - - [Fact] - public Task Method_Task_BlockBody_Throws() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - - throw new System.ApplicationException(); - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - throw new System.ApplicationException(); - } - } - """); - - [Fact] - public Task Method_Task_BlockBody_WithLocalFunction() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - if (GetTicks() > 0) - { - return; - } - - long GetTicks() - { - return System.DateTime.Now.Ticks; - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() - { - if (GetTicks() > 0) - { - return Task.CompletedTask; - } - - long GetTicks() - { - return System.DateTime.Now.Ticks; - } - - return Task.CompletedTask; - } - } - """); - - [Fact] - public Task Method_Task_BlockBody_WithLambda() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - System.Func getTicks = () => { - return System.DateTime.Now.Ticks; - }; - - if (getTicks() > 0) - { - return; - } - - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() - { - System.Func getTicks = () => { - return System.DateTime.Now.Ticks; - }; - - if (getTicks() > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - } - } - """); - - [Fact] - public Task Method_TaskOfT_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return 2; - } - - return 3; - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.FromResult(2); - } - - return Task.FromResult(3); - } - } - """); - - [Fact] - public Task Method_TaskOfT_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() => 2; - } - """, - """ - using System.Threading.Tasks; - - class C - { - Task Goo() => Task.FromResult(2); - } - """); - - [Fact] - public Task Method_Task_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - async Task {|CS1998:Goo|}() => Console.WriteLine("Hello"); - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - Task Goo() - { - Console.WriteLine("Hello"); - return Task.CompletedTask; - } - } - """); - - [Fact] - public Task LocalFunction_Task_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - async Task {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - Task Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - } - } - } - """); - - [Fact] - public Task LocalFunction_Task_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - async Task {|CS1998:Goo|}() => Console.WriteLine(1); - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Task Goo() { Console.WriteLine(1); return Task.CompletedTask; } - } - } - """); - - [Fact] - public Task LocalFunction_TaskOfT_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - async Task {|CS1998:Goo|}() - { - return 1; - } - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - Task Goo() - { - return Task.FromResult(1); - } - } - } - """); - - [Fact] - public Task LocalFunction_TaskOfT_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - async Task {|CS1998:Goo|}() => 1; - } - } - """, - """ - using System.Threading.Tasks; - - class C - { - public void M1() - { - Task Goo() => Task.FromResult(1); - } - } - """); - - [Fact] - public Task AnonymousFunction_Task_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = (Func)async {|CS1998:delegate|} { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - }; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = (Func)delegate - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - }; - } - } - """); - - [Fact] - public Task AnonymousFunction_TaskOfT_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = (Func>)async {|CS1998:delegate|} - { - return 1; - }; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = (Func>)delegate - { - return Task.FromResult(1); - }; - } - } - """); - - [Fact] - public Task SimpleLambda_TaskOfT_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = async x {|CS1998:=>|} 1; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = x => Task.FromResult(1); - } - } - """); - - [Fact] - public Task SimpleLambda_TaskOfT_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = async x {|CS1998:=>|} { - return 1; - }; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = x => - { - return Task.FromResult(1); - }; - } - } - """); - - [Fact] - public Task SimpleLambda_Task_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = async x {|CS1998:=>|} Console.WriteLine(1); - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = x => { Console.WriteLine(1); return Task.CompletedTask; }; - } - } - """); - - [Fact] - public Task SimpleLambda_Task_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = async x {|CS1998:=>|} - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - }; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = x => - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - }; - } - } - """); - - [Fact] - public Task ParenthesizedLambda_TaskOfT_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = async () {|CS1998:=>|} 1; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = () => Task.FromResult(1); - } - } - """); - - [Fact] - public Task ParenthesizedLambda_TaskOfT_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = async () {|CS1998:=>|} { - return 1; - }; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func> foo = () => - { - return Task.FromResult(1); - }; - } - } - """); - - [Fact] - public Task ParenthesizedLambda_Task_ExpressionBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = async () {|CS1998:=>|} Console.WriteLine(1); - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = () => { Console.WriteLine(1); return Task.CompletedTask; }; - } - } - """); - - [Fact] - public Task ParenthesizedLambda_Task_BlockBody() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = async () {|CS1998:=>|} - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - }; - } - } - """, - """ - using System; - using System.Threading.Tasks; - - class C - { - public void M1() - { - Func foo = () => - { - if (System.DateTime.Now.Ticks > 0) - { - return Task.CompletedTask; - } - - return Task.CompletedTask; - }; - } - } - """); - - [Fact] - public Task Method_Task_BlockBody_FullyQualified() - => VerifyCS.VerifyCodeFixAsync( - """ - class C - { - async System.Threading.Tasks.Task {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return; - } - } - } - """, - """ - class C - { - System.Threading.Tasks.Task Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return System.Threading.Tasks.Task.CompletedTask; - } - - return System.Threading.Tasks.Task.CompletedTask; - } - } - """); - - [Fact] - public Task Method_TaskOfT_BlockBody_FullyQualified() - => VerifyCS.VerifyCodeFixAsync( - """ - class C - { - async System.Threading.Tasks.Task {|CS1998:Goo|}() - { - if (System.DateTime.Now.Ticks > 0) - { - return 1; - } - - return 2; - } - } - """, - """ - class C - { - System.Threading.Tasks.Task Goo() - { - if (System.DateTime.Now.Ticks > 0) - { - return System.Threading.Tasks.Task.FromResult(1); - } - - return System.Threading.Tasks.Task.FromResult(2); - } - } - """); - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65536")] - public Task Method_TaskOfT_BlockBody_QualifyTaskFromResultType() - => VerifyCS.VerifyCodeFixAsync(""" - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - public async Task> {|CS1998:M|}() - { - return new int[0]; - } - } - """, """ - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - public Task> M() - { - return Task.FromResult>(new int[0]); - } - } - """); - - [Fact] - public async Task IAsyncEnumerable_Missing() - { - var source = """ - using System.Threading.Tasks; - using System.Collections.Generic; - - class C - { - async IAsyncEnumerable M() - { - yield return 1; - } - } - """ + CSharpTestBase.AsyncStreamsTypes; - - await new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = source, - ExpectedDiagnostics = - { - // /0/Test0.cs(7,33): 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. - DiagnosticResult.CompilerWarning("CS1998").WithSpan(6, 33, 6, 34), - }, - FixedCode = source, - }.RunAsync(); - } - - [Fact] - public async Task Method_AsyncVoid_Missing() - { - var source = """ - using System.Threading.Tasks; - - class C - { - async void M() - { - System.Console.WriteLine(1); - } - } - """; - - await new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = source, - ExpectedDiagnostics = - { - // /0/Test0.cs(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. - DiagnosticResult.CompilerWarning("CS1998").WithSpan(5, 16, 5, 17), - }, - FixedCode = source, - }.RunAsync(); - } - - [Fact] - public async Task ParenthesizedLambda_AsyncVoid_Missing() - { - var source = """ - using System; - using System.Threading.Tasks; - - class C - { - void M() - { - Action a = async () => Console.WriteLine(1); - } - } - """; - - await new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = source, - ExpectedDiagnostics = - { - // /0/Test0.cs(9,29): 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. - DiagnosticResult.CompilerWarning("CS1998").WithSpan(8, 29, 8, 31), - }, - FixedCode = source, - }.RunAsync(); - } - - [Fact] - public async Task SimpleLambda_AsyncVoid_Missing() - { - var source = """ - using System; - using System.Threading.Tasks; - - class C - { - void M() - { - Action a = async x => Console.WriteLine(x); - } - } - """; - - await new VerifyCS.Test - { - ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard21, - TestCode = source, - ExpectedDiagnostics = - { - // /0/Test0.cs(9,33): 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. - DiagnosticResult.CompilerWarning("CS1998").WithSpan(8, 33, 8, 35), - }, - FixedCode = source, - }.RunAsync(); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65380")] - public Task TestCloseBraceTrivia() - => VerifyCS.VerifyCodeFixAsync( - """ - using System; - using System.Threading.Tasks; - - public class Class1 - { - public async Task {|CS1998:Goo|}() - { - //Hello - Console.WriteLine("Goo"); - //World - } - } - """, - """ - using System; - using System.Threading.Tasks; - - public class Class1 - { - public Task Goo() - { - //Hello - Console.WriteLine("Goo"); - return Task.CompletedTask; - //World - } - } - """); -} diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs index e0e67a008e3..3cbecc86a5d 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/SimplifyInterpolation/SimplifyInterpolationTests.cs @@ -1056,10 +1056,65 @@ struct TypeNotImplementingIFormattable } """); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42936")] - public Task ToStringSimplificationIsNotOfferedOnRefStruct() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnRefStructIfInterpolatedStringHandlersUnavailable() + => TestMissingInRegularAndScriptAsync( + """ + class C + { + string M(RefStruct someValue) => $"Test: {someValue[||].ToString()}"; + } + + ref struct RefStruct + { + public override string ToString() => "A"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanIfInterpolatedStringHandlersUnavailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanIfInterpolatedStringHandlersUnavailable() => TestMissingInRegularAndScriptAsync( """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + class C + { + string M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnRefStructIfInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + class C { string M(RefStruct someValue) => $"Test: {someValue[||].ToString()}"; @@ -1071,6 +1126,223 @@ ref struct RefStruct } """); + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsOfferedOnReadOnlySpanOfCharIfInterpolatedStringHandlersAvailable() + => TestInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """, + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsOfferedOnSpanOfCharIfInterpolatedStringHandlersAvailable() + => TestInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(Span span) => $"Test: {span[||].ToString()}"; + } + """, + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(Span span) => $"Test: {span}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanOfIntIfInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanOfIntIfInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + string M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanIfTargetsFormattableStringAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + FormattableString M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanIfTargetsFormattableStringAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + FormattableString M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnReadOnlySpanIfTargetsIFormattableAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + IFormattable M(ReadOnlySpan span) => $"Test: {span[||].ToString()}"; + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80132")] + public Task ToStringSimplificationIsNotOfferedOnSpanIfTargetsIFormattableAndInterpolatedStringHandlersAvailable() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + namespace System + { + public ref struct Span { } + public ref struct ReadOnlySpan { } + } + + namespace System.Runtime.CompilerServices + { + public class InterpolatedStringHandlerAttribute { } + } + + class C + { + IFormattable M(Span span) => $"Test: {span[||].ToString()}"; + } + """); + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42936")] public Task PadLeftSimplificationIsStillOfferedOnRefStruct() => TestInRegularAndScriptAsync( @@ -1232,7 +1504,7 @@ public Task TestNotPassedToFormattableString1() """ class C { - void B() => M($"{args[||].Length.ToString()}"); + void B(string[] args) => M($"{args[||].Length.ToString()}"); void M(FormattableString fs) { @@ -1242,6 +1514,32 @@ void M(FormattableString fs) } """); + [Fact] + public Task TestNotPassedToIFormattable() + => TestMissingAsync( + """ + class C : ICustomFormatter, IFormatProvider + { + void B(string[] args) => M($"{args[||].Length.ToString()}"); + + void M(IFormattable fs) + { + fs.ToString(null, formatProvider: this); + } + + object? IFormatProvider.GetFormat(Type? formatType) + { + return formatType == typeof(ICustomFormatter) ? this : null; + } + + string ICustomFormatter.Format(string? format, object? arg, IFormatProvider? formatProvider) + { + Console.WriteLine(arg?.GetType()); + return ""; + } + } + """); + [Theory] [InlineData("DateTime", "ToLongDateString", "D")] [InlineData("DateTime", "ToShortDateString", "d")] diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs index c0ce194d899..d7f28579c60 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckTests.cs @@ -16,13 +16,9 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseCoalesceExpression; [Trait(Traits.Feature, Traits.Features.CodeActionsUseCoalesceExpression)] -public sealed class UseCoalesceExpressionForNullableTernaryConditionalCheckTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor +public sealed class UseCoalesceExpressionForNullableTernaryConditionalCheckTests(ITestOutputHelper logger) + : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger) { - public UseCoalesceExpressionForNullableTernaryConditionalCheckTests(ITestOutputHelper logger) - : base(logger) - { - } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new CSharpUseCoalesceExpressionForNullableTernaryConditionalCheckDiagnosticAnalyzer(), new UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider()); diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitObjectCreation/UseImplicitObjectCreationTests.cs similarity index 92% rename from src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationTests.cs rename to src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitObjectCreation/UseImplicitObjectCreationTests.cs index f21849ab4a6..656a00662c8 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitObjectCreation/UseImplicitObjectCreationTests.cs @@ -884,4 +884,75 @@ class C """, LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9, }.RunAsync(); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77670")] + public Task TestWithTask1() + => new VerifyCS.Test + { + TestCode = """ + using System.Threading.Tasks; + + class C + { + async Task Func() => [|new|] C(); + } + """, + FixedCode = """ + using System.Threading.Tasks; + + class C + { + async Task Func() => new(); + } + """, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9, + }.RunAsync(); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77670")] + public Task TestWithValueTask1() + => new VerifyCS.Test + { + TestCode = """ + using System.Threading.Tasks; + + class C + { + async ValueTask Func() => [|new|] C(); + } + """, + FixedCode = """ + using System.Threading.Tasks; + + class C + { + async ValueTask Func() => new(); + } + """, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77670")] + public Task TestWithValueTask2() + => new VerifyCS.Test + { + TestCode = """ + using System.Threading.Tasks; + + class C + { + ValueTask Func() => [|new|] ValueTask(new C()); + } + """, + FixedCode = """ + using System.Threading.Tasks; + + class C + { + ValueTask Func() => new(new C()); + } + """, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); } diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests.cs index f691b50c914..752d47982a6 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests.cs @@ -15,7 +15,7 @@ using Xunit; using Xunit.Abstractions; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseExplicitType; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitOrExplicitType; [Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)] public sealed partial class UseExplicitTypeTests(ITestOutputHelper logger) diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests_FixAllTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests_FixAllTests.cs index 579fa8661e1..f519af2ce8f 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests_FixAllTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseExplicitTypeTests_FixAllTests.cs @@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseExplicitType; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitOrExplicitType; public partial class UseExplicitTypeTests { @@ -81,8 +81,8 @@ class Program static int F(int x, int y) { int i1 = 0; - Program p = new Program(); - Tuple<bool, int> tuple = Tuple.Create(true, 1); + var p = new Program(); + var tuple = Tuple.Create(true, 1); return i1; } @@ -191,8 +191,8 @@ class Program static int F(int x, int y) { int i1 = 0; - Program p = new Program(); - Tuple<bool, int> tuple = Tuple.Create(true, 1); + var p = new Program(); + var tuple = Tuple.Create(true, 1); return i1; } @@ -206,8 +206,8 @@ class Program2 static int F(int x, int y) { int i2 = 0; - Program2 p2 = new Program2(); - Tuple<bool, int> tuple2 = Tuple.Create(true, 1); + var p2 = new Program2(); + var tuple2 = Tuple.Create(true, 1); return i2; } @@ -301,8 +301,8 @@ class Program static int F(int x, int y) { int i1 = 0; - Program p = new Program(); - Tuple<bool, int> tuple = Tuple.Create(true, 1); + var p = new Program(); + var tuple = Tuple.Create(true, 1); return i1; } @@ -316,8 +316,8 @@ class Program2 static int F(int x, int y) { int i2 = 0; - Program2 p2 = new Program2(); - Tuple<bool, int> tuple2 = Tuple.Create(true, 1); + var p2 = new Program2(); + var tuple2 = Tuple.Create(true, 1); return i2; } @@ -333,8 +333,8 @@ class Program2 static int F(int x, int y) { int i3 = 0; - Program2 p3 = new Program2(); - Tuple<bool, int> tuple3 = Tuple.Create(true, 1); + var p3 = new Program2(); + var tuple3 = Tuple.Create(true, 1); return i3; } @@ -359,6 +359,7 @@ class Program static int F(int x, int y) { {|FixAllInDocument:var|} p = this; + var p2 = this; var i1 = 0; var tuple = Tuple.Create(true, 1); @@ -379,7 +380,8 @@ class Program static int F(int x, int y) { Program p = this; - int i1 = 0; + Program p2 = this; + var i1 = 0; var tuple = Tuple.Create(true, 1); return i1; diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs index a2edd1f4c3f..42d3dfe99da 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests.cs @@ -16,7 +16,7 @@ using Xunit; using Xunit.Abstractions; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitType; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitOrExplicitType; [Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)] public sealed partial class UseImplicitTypeTests(ITestOutputHelper? logger = null) diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests_FixAllTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests_FixAllTests.cs index 3a16dd2dbb7..3e1f52b2d2d 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests_FixAllTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseImplicitOrExplicitType/UseImplicitTypeTests_FixAllTests.cs @@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitType; +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.UseImplicitOrExplicitType; public partial class UseImplicitTypeTests { @@ -81,8 +81,8 @@ class Program static int F(int x, int y) { var i1 = 0; - var p = new Program(); - var tuple = Tuple.Create(true, 1); + Program p = new Program(); + Tuple<bool, int> tuple = Tuple.Create(true, 1); return i1; } @@ -191,8 +191,8 @@ class Program static int F(int x, int y) { var i1 = 0; - var p = new Program(); - var tuple = Tuple.Create(true, 1); + Program p = new Program(); + Tuple<bool, int> tuple = Tuple.Create(true, 1); return i1; } @@ -206,8 +206,8 @@ class Program2 static int F(int x, int y) { var i2 = 0; - var p2 = new Program2(); - var tuple2 = Tuple.Create(true, 1); + Program2 p2 = new Program2(); + Tuple<bool, int> tuple2 = Tuple.Create(true, 1); return i2; } @@ -301,8 +301,8 @@ class Program static int F(int x, int y) { var i1 = 0; - var p = new Program(); - var tuple = Tuple.Create(true, 1); + Program p = new Program(); + Tuple<bool, int> tuple = Tuple.Create(true, 1); return i1; } @@ -316,8 +316,8 @@ class Program2 static int F(int x, int y) { var i2 = 0; - var p2 = new Program2(); - var tuple2 = Tuple.Create(true, 1); + Program2 p2 = new Program2(); + Tuple<bool, int> tuple2 = Tuple.Create(true, 1); return i2; } @@ -333,8 +333,8 @@ class Program2 static int F(int x, int y) { var i3 = 0; - var p3 = new Program2(); - var tuple3 = Tuple.Create(true, 1); + Program2 p3 = new Program2(); + Tuple<bool, int> tuple3 = Tuple.Create(true, 1); return i3; } diff --git a/src/roslyn/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs b/src/roslyn/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs index 44a84832f08..6416757e777 100644 --- a/src/roslyn/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs +++ b/src/roslyn/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.UseNullPropagation; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -48,6 +47,7 @@ private static Task TestMissingInRegularAndScriptAsync( => new VerifyCS.Test { TestCode = testCode, + FixedCode = testCode, LanguageVersion = languageVersion, }.RunAsync(); @@ -2664,4 +2664,119 @@ void X(string s) { } } """, languageVersion: LanguageVersion.CSharp14); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/64431")] + public Task TestUnconstrainedGenericType() + => TestMissingInRegularAndScriptAsync( + """ + public sealed class Element + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, x is null ? null : x.Key); + } + } + """, + languageVersion: LanguageVersion.CSharp14); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/64431")] + public Task TestInterfaceTypeConstrainedGenericType() + => TestMissingInRegularAndScriptAsync( + """ + public sealed class Element : System.IDisposable + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, x is null ? null : x.Key); + } + + public void Dispose() { } + } + """, + languageVersion: LanguageVersion.CSharp14); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/64431")] + public Task TestClassConstrainedGenericType() + => TestInRegularAndScriptAsync( + """ + public sealed class Element where T : class + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, [|x is null ? null : x.Key|]); + } + } + """, + """ + public sealed class Element where T : class + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, x?.Key); + } + } + """, + languageVersion: LanguageVersion.CSharp14); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/64431")] + public Task TestStructConstrainedGenericType() + => TestInRegularAndScriptAsync( + """ + public sealed class Element where T : struct + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, [|x is null ? null : x.Key|]); + } + } + """, + """ + public sealed class Element where T : struct + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, x?.Key); + } + } + """, + languageVersion: LanguageVersion.CSharp14); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/64431")] + public Task TestRefTypeConstrainedGenericType() + => TestInRegularAndScriptAsync( + """ + public sealed class Element where T : System.Exception + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, [|x is null ? null : x.Key|]); + } + } + """, + """ + public sealed class Element where T : System.Exception + { + public T Key { get; } + + public bool Equals(Element x) + { + return Equals(null, x?.Key); + } + } + """, + languageVersion: LanguageVersion.CSharp14); } diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs b/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs index e3ae127f772..ab070b6da8c 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs @@ -31,9 +31,9 @@ public static bool TryGetMappedOptions(string diagnosticId, string language, [No public static bool TryGetMappedFadingOption(string diagnosticId, [NotNullWhen(true)] out PerLanguageOption2? fadingOption) => s_diagnosticIdToFadingOptionMap.TryGetValue(diagnosticId, out fadingOption); - public static bool IsKnownIDEDiagnosticId(string diagnosticId) - => s_diagnosticIdToOptionMap.ContainsKey(diagnosticId) || - s_diagnosticIdToLanguageSpecificOptionsMap.Values.Any(map => map.ContainsKey(diagnosticId)); + public static ImmutableHashSet KnownIDEDiagnosticIds + => [.. s_diagnosticIdToOptionMap.Keys, + .. s_diagnosticIdToLanguageSpecificOptionsMap.Values.SelectMany(map => map.Keys)]; public static void AddOptionMapping(string diagnosticId, ImmutableHashSet options) { diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs index e8555ba286a..e968035ca61 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/AbstractRemoveUnnecessaryAttributeSuppressionsDiagnosticAnalyzer.cs @@ -68,7 +68,7 @@ protected sealed override void InitializeWorker(AnalysisContext context) protected sealed class CompilationAnalyzer(Compilation compilation, INamedTypeSymbol suppressMessageAttributeType) { - private readonly SuppressMessageAttributeState _state = new SuppressMessageAttributeState(compilation, suppressMessageAttributeType); + private readonly SuppressMessageAttributeState _state = new(compilation, suppressMessageAttributeType); public void AnalyzeAssemblyOrModuleAttribute(SyntaxNode attributeSyntax, SemanticModel model, Action reportDiagnostic, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs index feae0e74b51..b24f3b83f33 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationDiagnosticAnalyzer.cs @@ -36,13 +36,20 @@ protected override void InitializeWorker(AnalysisContext context) { var compilation = context.Compilation; var knownToStringFormats = Helpers.BuildKnownToStringFormatsLookupTable(compilation); - context.RegisterOperationAction(context => AnalyzeInterpolation(context, compilation.FormattableStringType(), knownToStringFormats), OperationKind.Interpolation); + + var readOnlySpanOfCharType = compilation.ReadOnlySpanOfTType()?.Construct(compilation.GetSpecialType(SpecialType.System_Char)); + var handlersAvailable = compilation.InterpolatedStringHandlerAttributeType() != null; + + context.RegisterOperationAction(context => AnalyzeInterpolation(context, compilation.FormattableStringType(), compilation.IFormattableType(), readOnlySpanOfCharType, knownToStringFormats, handlersAvailable), OperationKind.Interpolation); }); private void AnalyzeInterpolation( OperationAnalysisContext context, INamedTypeSymbol? formattableStringType, - ImmutableDictionary knownToStringFormats) + INamedTypeSymbol? iFormattableType, + INamedTypeSymbol? readOnlySpanOfCharType, + ImmutableDictionary knownToStringFormats, + bool handlersAvailable) { var option = context.GetAnalyzerOptions().PreferSimplifiedInterpolation; @@ -55,7 +62,7 @@ private void AnalyzeInterpolation( // Formattable strings can observe the inner types of the arguments passed to them. So we can't safely change // to drop ToString in that. if (interpolation.Parent is IInterpolatedStringOperation { Parent: IConversionOperation { Type: { } convertedType } conversion } && - convertedType.Equals(formattableStringType)) + (convertedType.Equals(formattableStringType) || convertedType.Equals(iFormattableType))) { // One exception to this is calling directly into FormattableString.Invariant. That method has known good // behavior that is fine to continue calling into. @@ -68,7 +75,7 @@ private void AnalyzeInterpolation( } this.Helpers.UnwrapInterpolation( - this.VirtualCharService, this.SyntaxFacts, interpolation, knownToStringFormats, out _, out var alignment, out _, + this.VirtualCharService, this.SyntaxFacts, interpolation, knownToStringFormats, readOnlySpanOfCharType, handlersAvailable, out _, out var alignment, out _, out var formatString, out var unnecessaryLocations); if (alignment == null && formatString == null) diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs index dfe238707ed..a66b3581257 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/SimplifyInterpolation/AbstractSimplifyInterpolationHelpers.cs @@ -65,6 +65,8 @@ public void UnwrapInterpolation( ISyntaxFacts syntaxFacts, IInterpolationOperation interpolation, ImmutableDictionary knownToStringFormats, + INamedTypeSymbol? readOnlySpanOfCharType, + bool handlersAvailable, out TExpressionSyntax? unwrapped, out TExpressionSyntax? alignment, out bool negate, @@ -82,7 +84,7 @@ public void UnwrapInterpolation( UnwrapAlignmentPadding(expression, out expression, out alignment, out negate, unnecessarySpans); if (interpolation.FormatString == null) - UnwrapFormatString(virtualCharService, syntaxFacts, expression, knownToStringFormats, out expression, out formatString, unnecessarySpans); + UnwrapFormatString(virtualCharService, syntaxFacts, expression, knownToStringFormats, readOnlySpanOfCharType, handlersAvailable, out expression, out formatString, unnecessarySpans); unwrapped = GetPreservedInterpolationExpressionSyntax(expression) as TExpressionSyntax; @@ -118,6 +120,8 @@ private void UnwrapFormatString( ISyntaxFacts syntaxFacts, IOperation expression, ImmutableDictionary knownToStringFormats, + INamedTypeSymbol? readOnlySpanOfCharType, + bool handlersAvailable, out IOperation unwrapped, out string? formatString, ArrayBuilder unnecessarySpans) @@ -128,41 +132,43 @@ private void UnwrapFormatString( HasNonImplicitInstance(invocation, out var instance) && !syntaxFacts.IsBaseExpression(instance.Syntax)) { - if (targetMethod.Name == nameof(ToString) && - instance.Type is { IsRefLikeType: false }) + if (targetMethod.Name == nameof(ToString)) { - if (invocation.Arguments.Length == 1 - || (invocation.Arguments.Length == 2 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 1))) + // If type of instance is not ref-like type or is {ReadOnly}Span that is allowed in interpolated strings in .NET 6+ + if (instance.Type is { IsRefLikeType: false } || IsRefLikeTypeAllowed(instance.Type)) { - if (invocation.Arguments[0].Value is ILiteralOperation { ConstantValue: { HasValue: true, Value: string value } } literal && - FindType(expression.SemanticModel) is { } systemIFormattable && - instance.Type.Implements(systemIFormattable)) + if (invocation.Arguments.Length == 1 + || (invocation.Arguments.Length == 2 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 1))) { + if (invocation.Arguments[0].Value is ILiteralOperation { ConstantValue: { HasValue: true, Value: string value } } literal && + FindType(expression.SemanticModel) is { } systemIFormattable && + instance.Type.Implements(systemIFormattable)) + { + unwrapped = instance; + formatString = value; + + unnecessarySpans.AddRange(invocation.Syntax.Span + .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan) + .Subtract(GetSpanWithinLiteralQuotes(virtualCharService, literal.Syntax.GetFirstToken()))); + return; + } + } + + if (IsObjectToStringOverride(invocation.TargetMethod) + || (invocation.Arguments.Length == 1 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 0))) + { + // A call to `.ToString()` at the end of the interpolation. This is unnecessary. + // Just remove entirely. unwrapped = instance; - formatString = value; + formatString = ""; unnecessarySpans.AddRange(invocation.Syntax.Span - .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan) - .Subtract(GetSpanWithinLiteralQuotes(virtualCharService, literal.Syntax.GetFirstToken()))); + .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan)); return; } } - - if (IsObjectToStringOverride(invocation.TargetMethod) - || (invocation.Arguments.Length == 1 && UsesInvariantCultureReferenceInsideFormattableStringInvariant(invocation, formatProviderArgumentIndex: 0))) - { - // A call to `.ToString()` at the end of the interpolation. This is unnecessary. - // Just remove entirely. - unwrapped = instance; - formatString = ""; - - unnecessarySpans.AddRange(invocation.Syntax.Span - .Subtract(GetPreservedInterpolationExpressionSyntax(instance).FullSpan)); - return; - } } - - if (knownToStringFormats.TryGetValue(targetMethod, out var format)) + else if (knownToStringFormats.TryGetValue(targetMethod, out var format)) { // A call to a known ToString-like method, e.g. `DateTime.ToLongDateString()` // We replace this call with predefined format specifier @@ -177,6 +183,13 @@ private void UnwrapFormatString( unwrapped = expression; formatString = null; + + bool IsRefLikeTypeAllowed([NotNullWhen(true)] ITypeSymbol? type) + { + var compilation = expression.SemanticModel.Compilation; + // {ReadOnly}Span is allowed if interpolated string handlers are available in the compilation (.NET 6+) + return handlersAvailable && compilation.HasImplicitConversion(type, readOnlySpanOfCharType); + } } private static bool IsObjectToStringOverride(IMethodSymbol method) diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs index 682ab67dc11..0425165f060 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs @@ -42,7 +42,6 @@ public readonly record struct AnalysisResult( protected UpdateExpressionState State; protected TObjectCreationExpressionSyntax _objectCreationExpression = null!; - protected bool _analyzeForCollectionExpression; protected ISyntaxFacts SyntaxFacts => this.State.SyntaxFacts; protected SemanticModel SemanticModel => this.State.SemanticModel; @@ -65,19 +64,16 @@ public void Dispose() public void Initialize( UpdateExpressionState state, - TObjectCreationExpressionSyntax objectCreationExpression, - bool analyzeForCollectionExpression) + TObjectCreationExpressionSyntax objectCreationExpression) { State = state; _objectCreationExpression = objectCreationExpression; - _analyzeForCollectionExpression = analyzeForCollectionExpression; } - protected void Clear() + protected virtual void Clear() { State = default; _objectCreationExpression = null!; - _analyzeForCollectionExpression = false; } protected AnalysisResult AnalyzeWorker(CancellationToken cancellationToken) @@ -93,11 +89,10 @@ protected AnalysisResult AnalyzeWorker(CancellationToken cancellationToken) return new(preMatches.ToImmutableAndClear(), postMatches.ToImmutableAndClear(), mayChangeSemantics); } - protected UpdateExpressionState? TryInitializeState( + protected UpdateExpressionState TryInitializeState( SemanticModel semanticModel, ISyntaxFacts syntaxFacts, TObjectCreationExpressionSyntax rootExpression, - bool analyzeForCollectionExpression, CancellationToken cancellationToken) { var statement = rootExpression.FirstAncestorOrSelf()!; @@ -107,18 +102,11 @@ protected AnalysisResult AnalyzeWorker(CancellationToken cancellationToken) TryInitializeVariableDeclarationCase(semanticModel, syntaxFacts, rootExpression, statement, cancellationToken) ?? TryInitializeAssignmentCase(semanticModel, syntaxFacts, rootExpression, statement, cancellationToken); if (result != null) - return result; + return result.Value; } - // Even if the above cases didn't work, we always support converting a `new List()` collection over to - // a collection expression. We just won't analyze later statements. - if (analyzeForCollectionExpression) - { - return new UpdateExpressionState( - semanticModel, syntaxFacts, rootExpression, valuePattern: default, initializedSymbol: null); - } - - return null; + return new UpdateExpressionState( + semanticModel, syntaxFacts, rootExpression, valuePattern: default, initializedSymbol: null); } private UpdateExpressionState? TryInitializeVariableDeclarationCase( diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs index 86321d0d833..9020d3fa328 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -11,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.UseCollectionExpression; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseCollectionInitializer; @@ -50,6 +48,8 @@ internal abstract class AbstractUseCollectionInitializerAnalyzer< TVariableDeclaratorSyntax, TAnalyzer>, new() { + protected bool _analyzeForCollectionExpression; + protected abstract bool IsComplexElementInitializer(SyntaxNode expression); protected abstract bool HasExistingInvalidInitializerForCollection(); protected abstract bool AnalyzeMatchesAndCollectionConstructorForCollectionExpression( @@ -60,6 +60,12 @@ protected abstract bool AnalyzeMatchesAndCollectionConstructorForCollectionExpre protected abstract IUpdateExpressionSyntaxHelper SyntaxHelper { get; } + protected override void Clear() + { + base.Clear(); + _analyzeForCollectionExpression = false; + } + public AnalysisResult Analyze( SemanticModel semanticModel, ISyntaxFacts syntaxFacts, @@ -67,11 +73,16 @@ public AnalysisResult Analyze( bool analyzeForCollectionExpression, CancellationToken cancellationToken) { - var state = TryInitializeState(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression, cancellationToken); - if (state is null) + _analyzeForCollectionExpression = analyzeForCollectionExpression; + var state = TryInitializeState(semanticModel, syntaxFacts, objectCreationExpression, cancellationToken); + + // If we didn't find something we're assigned to, then we normally can't continue. However, we always support + // converting a `new List()` collection over to a collection expression. We just won't analyze later + // statements. + if (state.ValuePattern == default && !analyzeForCollectionExpression) return default; - this.Initialize(state.Value, objectCreationExpression, analyzeForCollectionExpression); + this.Initialize(state, objectCreationExpression); var (preMatches, postMatches, mayChangeSemantics) = this.AnalyzeWorker(cancellationToken); // If analysis failed entirely, immediately bail out. diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/UpdateExpressionState.cs b/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/UpdateExpressionState.cs index 274f8c9dc8f..bb8f49943b6 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/UpdateExpressionState.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/UseCollectionInitializer/UpdateExpressionState.cs @@ -20,7 +20,12 @@ namespace Microsoft.CodeAnalysis.UseCollectionInitializer; /// internal readonly struct UpdateExpressionState< TExpressionSyntax, - TStatementSyntax> + TStatementSyntax>( + SemanticModel semanticModel, + ISyntaxFacts syntaxFacts, + TExpressionSyntax startExpression, + SyntaxNodeOrToken valuePattern, + ISymbol? initializedSymbol) where TExpressionSyntax : SyntaxNode where TStatementSyntax : SyntaxNode { @@ -31,44 +36,29 @@ internal readonly struct UpdateExpressionState< (nameof(Enumerable.Append), isLinq: true), ]; - public readonly SemanticModel SemanticModel; - public readonly ISyntaxFacts SyntaxFacts; + public readonly SemanticModel SemanticModel = semanticModel; + public readonly ISyntaxFacts SyntaxFacts = syntaxFacts; /// /// The original object-creation or collection-builder-creation expression. /// - public readonly TExpressionSyntax StartExpression; + public readonly TExpressionSyntax StartExpression = startExpression; /// /// The statement containing /// - public readonly TStatementSyntax? ContainingStatement; + public readonly TStatementSyntax? ContainingStatement = startExpression.FirstAncestorOrSelf()!; /// /// The name of the value being mutated. It is whatever the new object-creation or collection-builder is assigned to. /// - public readonly SyntaxNodeOrToken ValuePattern; + public readonly SyntaxNodeOrToken ValuePattern = valuePattern; /// /// If a different symbol was initialized (for example, a field rather than a local) this will be that symbol. This /// only applies to the object-creation case. /// - public readonly ISymbol? InitializedSymbol; - - public UpdateExpressionState( - SemanticModel semanticModel, - ISyntaxFacts syntaxFacts, - TExpressionSyntax startExpression, - SyntaxNodeOrToken valuePattern, - ISymbol? initializedSymbol) - { - SemanticModel = semanticModel; - SyntaxFacts = syntaxFacts; - StartExpression = startExpression; - ContainingStatement = startExpression.FirstAncestorOrSelf()!; - ValuePattern = valuePattern; - InitializedSymbol = initializedSymbol; - } + public readonly ISymbol? InitializedSymbol = initializedSymbol; public IEnumerable GetSubsequentStatements() => ContainingStatement is null diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs index ab6ac47e21d..fd6b8ce86af 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs @@ -215,6 +215,19 @@ private void AnalyzeTernaryConditionalExpressionAndReportDiagnostic( if (memberSymbol is IMethodSymbol) return null; + // we're converting from `x.M` to `x?.M`. This is not legal if 'M' is an unconstrained type parameter as + // the lang/compiler doesn't know what final type to make out of this. + + var memberType = semanticModel.GetTypeInfo(whenPartToCheck, cancellationToken).Type; + if (memberType is null or ITypeParameterSymbol + { + IsReferenceType: false, + IsValueType: false, + }) + { + return null; + } + // `x == null ? x : x.Value` will be converted to just 'x'. if (UseNullPropagationHelpers.IsSystemNullableValueProperty(memberSymbol)) isTrivialNullableValueAccess = true; diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs index e13de8a6c92..d2a85c0cf4f 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/AbstractUseObjectInitializerDiagnosticAnalyzer.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.UseObjectInitializer; diff --git a/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs b/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs index 6760960332f..21ebd6e7b4f 100644 --- a/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs +++ b/src/roslyn/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs @@ -52,11 +52,13 @@ public ImmutableArray(string language) where TLanguageService : ILanguageService - => _document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(language).GetRequiredService(); + => _document.Project.Solution.GetRequiredLanguageService(language); private bool ClashesWithExistingConstructor() { diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs index 51855dbec33..c008fab550a 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.CodeAction.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateEnumMember; @@ -21,7 +22,7 @@ private sealed partial class GenerateEnumMemberCodeAction(Document document, Sta protected override async Task GetChangedDocumentAsync(CancellationToken cancellationToken) { - var languageServices = _document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(_state.TypeToGenerateIn.Language); + var languageServices = _document.Project.Solution.GetExtendedLanguageServices(_state.TypeToGenerateIn.Language); var codeGenerator = languageServices.GetRequiredService(); var semanticFacts = languageServices.GetRequiredService(); diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs index 3a0fca8de4c..efe083d293c 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs @@ -118,9 +118,7 @@ public async ValueTask GenerateMethodAsync( methodKind: State.MethodKind); // Ensure no conflicts between type parameter names and parameter names. - var languageServiceProvider = Document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(State.TypeToGenerateIn.Language); - - var syntaxFacts = languageServiceProvider.GetService(); + var syntaxFacts = Document.Project.Solution.GetLanguageService(State.TypeToGenerateIn.Language); var equalityComparer = syntaxFacts.StringComparer; var reservedParameterNames = DetermineParameterNames(cancellationToken) diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs index ce6a1619e54..24c4323b4a1 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.State.cs @@ -82,7 +82,7 @@ protected async Task TryFinishInitializingStateAsync(TService service, Sem .GetMembers(IdentifierToken.ValueText) .OfType(); - var destinationProvider = document.Project.Solution.Workspace.Services.GetExtendedLanguageServices(TypeToGenerateIn.Language); + var destinationProvider = document.Project.Solution.GetExtendedLanguageServices(TypeToGenerateIn.Language); var syntaxFacts = destinationProvider.GetService(); var syntaxFactory = destinationProvider.GetService(); diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs index 2d502a68a92..378e204090f 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; @@ -48,9 +49,7 @@ protected async ValueTask> GetActionsAsync(Document d if (canGenerateAbstractly) result.Add(new GenerateParameterizedMemberCodeAction((TService)this, document, state, isAbstract: true, generateProperty: false)); - var semanticFacts = document.Project.Solution.Workspace.Services - .GetExtendedLanguageServices(state.TypeToGenerateIn.Language) - .GetRequiredService(); + var semanticFacts = document.Project.Solution.GetRequiredLanguageService(state.TypeToGenerateIn.Language); if (semanticFacts.SupportsParameterizedProperties && state.InvocationExpressionOpt != null) diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs index b5911cb66c3..88f5603c5e8 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/SimplifyInterpolation/AbstractSimplifyInterpolationCodeFixProvider.cs @@ -48,12 +48,16 @@ protected override async Task FixAllAsync( SyntaxEditor editor, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var compilation = semanticModel.Compilation; var generator = editor.Generator; var generatorInternal = document.GetRequiredLanguageService(); var helpers = this.Helpers; var knownToStringFormats = helpers.BuildKnownToStringFormatsLookupTable(semanticModel.Compilation); + var readOnlySpanOfCharType = compilation.ReadOnlySpanOfTType()?.Construct(compilation.GetSpecialType(SpecialType.System_Char)); + var handlersAvailable = compilation.InterpolatedStringHandlerAttributeType() != null; + foreach (var diagnostic in diagnostics) { var node = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); @@ -64,7 +68,7 @@ protected override async Task FixAllAsync( helpers.UnwrapInterpolation( document.GetRequiredLanguageService(), document.GetRequiredLanguageService(), - interpolation, knownToStringFormats, out var unwrapped, out var alignment, out var negate, out var formatString, out _); + interpolation, knownToStringFormats, readOnlySpanOfCharType, handlersAvailable, out var unwrapped, out var alignment, out var negate, out var formatString, out _); if (unwrapped == null) continue; diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 7ef21eba865..28b0e34db30 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -515,6 +515,7 @@ private static async ValueTask IsWrittenToOutsideOfConstructorOrPropertyAs // We do need a setter return true; + // Remove after .NET 10, https://github.com/dotnet/roslyn/issues/80198 #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously async ValueTask IsWrittenToAsync(ReferenceLocation loc) { diff --git a/src/roslyn/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs b/src/roslyn/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs index 7a994f7fe81..3e3a0eecc86 100644 --- a/src/roslyn/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs +++ b/src/roslyn/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs @@ -63,6 +63,10 @@ protected override async Task FixAllAsync( ? g.CoalesceExpression(conditionExpression, syntaxFacts.WalkDownParentheses(currentWhenTrue)) : g.CoalesceExpression(conditionExpression, syntaxFacts.WalkDownParentheses(currentWhenFalse)); + // We may be moving from `a == null ? b : a` to `a ?? b`. In this case, we want to ensure that the + // space after the 'b' can be cleaned up if needed. + coalesceExpression = coalesceExpression.WithAppendedTrailingTrivia(syntaxFacts.ElasticMarker); + if (semanticFacts.IsInExpressionTree( semanticModel, conditionalExpression, expressionTypeOpt, cancellationToken)) { diff --git a/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs b/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs index bb91cf3e508..efad3c25d9e 100644 --- a/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs +++ b/src/roslyn/src/CodeStyle/CSharp/Tests/AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_OptionHelpers.cs @@ -34,20 +34,20 @@ internal static (OptionKey2, object) SingleOption(PerLanguageOption2 (new OptionKey2(option, language), codeStyle); internal OptionsCollection Option(Option2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(Option2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; internal OptionsCollection Option(PerLanguageOption2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(PerLanguageOption2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; internal OptionsCollection Option(Option2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; internal OptionsCollection Option(PerLanguageOption2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; } diff --git a/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs b/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs index a62af922ab2..d5b5649ba4f 100644 --- a/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs +++ b/src/roslyn/src/CodeStyle/Core/CodeFixes/Host/Mef/CodeStyleHostLanguageServices.MefHostExportProvider.cs @@ -10,10 +10,8 @@ namespace Microsoft.CodeAnalysis.Host; internal sealed partial class CodeStyleHostLanguageServices : HostLanguageServices { - private static readonly ConditionalWeakTable s_mappedLanguageServices = - new ConditionalWeakTable(); - private static readonly ConditionalWeakTable s_exportProvidersByLanguageCache = - new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_mappedLanguageServices = new(); + private static readonly ConditionalWeakTable s_exportProvidersByLanguageCache = new(); private readonly HostLanguageServices _hostLanguageServices; private readonly HostLanguageServices _codeStyleLanguageServices; @@ -35,7 +33,7 @@ public static CodeStyleHostLanguageServices GetRequiredMappedCodeStyleLanguageSe => s_mappedLanguageServices.GetValue(hostLanguageServices, Create); private static CodeStyleHostLanguageServices Create(HostLanguageServices hostLanguageServices) - => new CodeStyleHostLanguageServices(hostLanguageServices); + => new(hostLanguageServices); public override HostWorkspaceServices WorkspaceServices => _hostLanguageServices.WorkspaceServices; diff --git a/src/roslyn/src/CodeStyle/Tools/Program.cs b/src/roslyn/src/CodeStyle/Tools/Program.cs index 19979aa8d9c..ef1992aaf70 100644 --- a/src/roslyn/src/CodeStyle/Tools/Program.cs +++ b/src/roslyn/src/CodeStyle/Tools/Program.cs @@ -255,9 +255,9 @@ and an implied numerical option (such as '4') --> <_GlobalAnalyzerConfigFile_MicrosoftCodeAnalysis{language}CodeStyle Condition="'$(_GlobalAnalyzerConfigFileName_MicrosoftCodeAnalysis{language}CodeStyle)' != ''">$(_GlobalAnalyzerConfigDir_MicrosoftCodeAnalysis{language}CodeStyle)\$(_GlobalAnalyzerConfigFileName_MicrosoftCodeAnalysis{language}CodeStyle) - + + ('$(AnalysisLevelStyle)' != '$(AnalysisLevel)' or '$(AnalysisModeStyle)' != '$(AnalysisMode)' or ('$(EffectiveAnalysisLevelStyle)' != '' and $([MSBuild]::VersionGreaterThanOrEquals('$(EffectiveAnalysisLevelStyle)', '11.0'))))"> diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 3b1f53346f8..aa9a259053f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -3056,7 +3056,13 @@ private void GetEscapeValuesForOldRules( escapeValues.Add(new EscapeValue(parameter, argument, EscapeLevel.CallingMethod, isRefEscape: false)); } - if (parameter.RefKind != RefKind.None) + // https://github.com/dotnet/csharpstandard/blob/0ad29bf615b18ae463d92ef64f557eeb007b76f1/standard/variables.md#9723-parameter-ref-safe-context + // For a parameter `p`: + // - If `p` is a reference or input parameter, its ref-safe-context is the caller-context. If `p` is an input parameter, it can’t be returned as a writable `ref` but can be returned as `ref readonly`. + // - If `p` is an output parameter, its ref-safe-context is the caller-context. + // - Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-context is the function-member. + // - Otherwise, the parameter is a value parameter, and its ref-safe-context is the function-member. + if (parameter.RefKind != RefKind.None && !parameter.IsThis) { escapeValues.Add(new EscapeValue(parameter, argument, EscapeLevel.CallingMethod, isRefEscape: true)); } @@ -3761,6 +3767,11 @@ internal SafeContext GetRefEscape(BoundExpression expr, SafeContext localScopeDe { var call = (BoundCall)expr; + if (call.IsErroneousNode) + { + return SafeContext.CallingMethod; + } + var methodSymbol = call.Method; if (methodSymbol.RefKind == RefKind.None) { @@ -3820,6 +3831,11 @@ internal SafeContext GetRefEscape(BoundExpression expr, SafeContext localScopeDe return SafeContext.CallingMethod; case BoundCall call: + if (call.IsErroneousNode) + { + return SafeContext.CallingMethod; + } + var methodSymbol = call.Method; if (methodSymbol.RefKind == RefKind.None) { @@ -4043,6 +4059,11 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, SafeContext { var call = (BoundCall)expr; + if (call.IsErroneousNode) + { + return true; + } + var methodSymbol = call.Method; if (methodSymbol.RefKind == RefKind.None) { @@ -4108,6 +4129,11 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, SafeContext return true; case BoundCall call: + if (call.IsErroneousNode) + { + return true; + } + var methodSymbol = call.Method; if (methodSymbol.RefKind == RefKind.None) { @@ -4405,6 +4431,11 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe { var call = (BoundCall)expr; + if (call.IsErroneousNode) + { + return SafeContext.CallingMethod; + } + return GetInvocationEscapeScope( MethodInvocationInfo.FromCall(call), localScopeDepth, @@ -4451,6 +4482,11 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe return localScopeDepth; case BoundCall call: + if (call.IsErroneousNode) + { + return SafeContext.CallingMethod; + } + return GetInvocationEscapeScope( MethodInvocationInfo.FromCall(call, implicitIndexerAccess.Receiver), localScopeDepth, @@ -5082,6 +5118,11 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext case BoundKind.Call: { var call = (BoundCall)expr; + if (call.IsErroneousNode) + { + return true; + } + var methodSymbol = call.Method; return CheckInvocationEscape( @@ -5146,6 +5187,11 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, SafeContext return false; case BoundCall call: + if (call.IsErroneousNode) + { + return true; + } + var methodSymbol = call.Method; return CheckInvocationEscape( @@ -5820,7 +5866,7 @@ SafeContext getPartsScope(BoundInterpolatedString interpolatedString, SafeContex foreach (var part in interpolatedString.Parts) { - if (part is not BoundCall call) + if (part is not BoundCall { IsErroneousNode: false } call) { // Dynamic calls cannot have ref struct parameters. continue; @@ -5862,7 +5908,7 @@ bool checkParts(BoundInterpolatedString interpolatedString, SafeContext escapeFr { foreach (var part in interpolatedString.Parts) { - if (part is not BoundCall call) + if (part is not BoundCall { IsErroneousNode: false } call) { // Dynamic calls cannot have ref struct parameters. continue; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 2788139ff97..9c16d61fef4 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -93,6 +93,7 @@ private bool CouldBeAwaited(BoundExpression expression) } var call = (BoundCall)expression; + Debug.Assert(!call.IsErroneousNode); // First check if the target method is async. if ((object)call.Method != null && call.Method.IsAsync) @@ -591,6 +592,8 @@ private bool GetGetAwaiterMethod(BoundExpression expression, SyntaxNode node, Bi } var call = (BoundCall)getAwaiterCall; + Debug.Assert(!call.IsErroneousNode); + var getAwaiterMethod = call.Method; if (getAwaiterMethod is ErrorMethodSymbol || call.Expanded || HasOptionalParameters(getAwaiterMethod) || // We might have been able to resolve a GetAwaiter overload with optional parameters, so check for that here @@ -699,6 +702,8 @@ private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode no } var call = (BoundCall)getAwaiterGetResultCall; + Debug.Assert(!call.IsErroneousNode); + getResultMethod = call.Method; if (getResultMethod.IsExtensionMethod || getResultMethod.GetIsNewExtensionMember()) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs index bc1173c91df..148146b5054 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -224,12 +225,6 @@ private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax { CheckFeatureAvailability(syntax, MessageID.IDS_FeatureExtensions, diagnostics); - if (containerOpt is not NamedTypeSymbol namedContainer) - { - ambiguityWinner = null; - return ImmutableArray.Empty; - } - int arity = 0; TypeArgumentListSyntax? typeArgumentListSyntax = null; CrefParameterListSyntax? parameters = null; @@ -256,7 +251,7 @@ private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax TypeArgumentListSyntax? extensionTypeArguments = syntax.TypeArgumentList; int extensionArity = extensionTypeArguments?.Arguments.Count ?? 0; - ImmutableArray sortedSymbols = computeSortedAndFilteredCrefExtensionMembers(namedContainer, memberName, extensionArity, arity, extensionTypeArguments, diagnostics, syntax); + ImmutableArray sortedSymbols = computeSortedAndFilteredCrefExtensionMembers(containerOpt, memberName, extensionArity, arity, extensionTypeArguments, diagnostics, syntax); if (sortedSymbols.IsDefaultOrEmpty) { @@ -268,7 +263,7 @@ private ImmutableArray BindExtensionMemberCref(ExtensionMemberCrefSyntax return ProcessCrefMemberLookupResults(sortedSymbols, arity, syntax, typeArgumentListSyntax, parameters, out ambiguityWinner, diagnostics); - ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSymbol container, string name, int extensionArity, int arity, TypeArgumentListSyntax? extensionTypeArguments, BindingDiagnosticBag diagnostics, ExtensionMemberCrefSyntax syntax) + ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamespaceOrTypeSymbol? containerOpt, string name, int extensionArity, int arity, TypeArgumentListSyntax? extensionTypeArguments, BindingDiagnosticBag diagnostics, ExtensionMemberCrefSyntax syntax) { Debug.Assert(name is not null); @@ -295,9 +290,12 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym CompoundUseSiteInfo useSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); ArrayBuilder? sortedSymbolsBuilder = null; - foreach (var nested in container.GetTypeMembers()) + foreach (var nested in candidateTypes(containerOpt)) { - if (!nested.IsExtension || nested.Arity != extensionArity || nested.ExtensionParameter is null) + if (!nested.IsExtension + || nested.Arity != extensionArity + || nested.ExtensionParameter is null + || nested is not { ContainingType: { ContainingType: null } }) // only consider extension blocks in top-level types { continue; } @@ -364,6 +362,23 @@ ImmutableArray computeSortedAndFilteredCrefExtensionMembers(NamedTypeSym } return sortedSymbolsBuilder.ToImmutableAndFree(); + + ImmutableArray candidateTypes(NamespaceOrTypeSymbol? containerOpt) + { + if (containerOpt is NamedTypeSymbol namedType) + { + return namedType.GetTypeMembers(""); + } + + NamedTypeSymbol? containingType = ContainingType; + if (containingType is null) + { + return []; + } + + NamedTypeSymbol? enclosingType = containingType.IsExtension ? containingType.ContainingType : containingType; + return enclosingType?.GetTypeMembers("") ?? []; + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index cc20ba28415..c6dc865d62e 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -504,7 +504,7 @@ internal BoundParameterEqualsValue BindParameterDefaultValue( Debug.Assert(this.InParameterDefaultValue); Debug.Assert(this.ContainingMemberOrLambda.Kind == SymbolKind.Method || this.ContainingMemberOrLambda.Kind == SymbolKind.Property - || this.ContainingMemberOrLambda is TypeSymbol { IsExtension: true }); + || this.ContainingMemberOrLambda is NamedTypeSymbol { IsExtension: true }); // UNDONE: The binding and conversion has to be executed in a checked context. Binder defaultValueBinder = this.GetBinder(defaultValueSyntax); @@ -3792,7 +3792,7 @@ BoundExpression bindInterpolatedStringHandlerInMemberCall( break; case BoundInterpolatedStringArgumentPlaceholder.ExtensionReceiver: Debug.Assert(methodResult.Member.GetIsNewExtensionMember()); - var receiverParameter = ((TypeSymbol)methodResult.Member.ContainingSymbol).ExtensionParameter; + var receiverParameter = ((NamedTypeSymbol)methodResult.Member.ContainingSymbol).ExtensionParameter; Debug.Assert(receiverParameter is not null); refKind = receiverParameter.RefKind; placeholderType = receiverParameter.Type; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index edc5a1bdea0..9145404c3ed 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -147,7 +147,7 @@ internal BoundExpression MakeInvocationExpression( // the error has already been reported by BindInvocationExpression Debug.Assert(diagnostics.DiagnosticBag is null || diagnostics.HasAnyErrors()); - result = CreateBadCall(node, boundExpression, LookupResultKind.Viable, analyzedArguments); + result = CreateBadCall(node, boundExpression, LookupResultKind.NotInvocable, analyzedArguments); } result.WasCompilerGenerated = true; @@ -369,7 +369,7 @@ private BoundExpression BindInvocationExpression( { if (ReportDelegateInvokeUseSiteDiagnostic(diagnostics, delegateType, node: node)) { - return CreateBadCall(node, boundExpression, LookupResultKind.Viable, analyzedArguments); + return CreateBadCall(node, boundExpression, LookupResultKind.NotInvocable, analyzedArguments); } result = BindDelegateInvocation(node, expression, methodName, boundExpression, analyzedArguments, diagnostics, queryClause, delegateType); @@ -748,6 +748,8 @@ private BoundExpression BindMethodGroupInvocation( ImmutableArray originalMethods; LookupResultKind resultKind; ImmutableArray typeArguments; + BoundExpression receiverOpt = methodGroup.ReceiverOpt; + if (resolution.OverloadResolutionResult != null) { originalMethods = GetOriginalMethods(resolution.OverloadResolutionResult); @@ -759,12 +761,25 @@ private BoundExpression BindMethodGroupInvocation( originalMethods = methodGroup.Methods; resultKind = methodGroup.ResultKind; typeArguments = methodGroup.TypeArgumentsOpt; + + if (originalMethods.IsEmpty && methodGroup.LookupSymbolOpt is { }) + { + Debug.Assert(methodGroup.LookupSymbolOpt is not MethodSymbol); + + // Create receiver as BindMemberAccessBadResult does + receiverOpt = new BoundBadExpression( + methodGroup.Syntax, + methodGroup.ResultKind, + [methodGroup.LookupSymbolOpt], + receiverOpt == null ? [] : [receiverOpt], + GetNonMethodMemberType(methodGroup.LookupSymbolOpt)); + } } result = CreateBadCall( syntax, methodName, - methodGroup.ReceiverOpt, + receiverOpt, originalMethods, resultKind, typeArguments, diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index a5259fae622..18387d0a600 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -682,6 +682,11 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind private static BoundExpression? ExtractCastInvocation(BoundCall invocation) { + if (invocation.IsErroneousNode) + { + return null; + } + int index = invocation.InvokedAsExtensionMethod ? 1 : 0; var c1 = invocation.Arguments[index] as BoundConversion; var l1 = c1 != null ? c1.Operand as BoundLambda : null; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 9916068baa3..c6b8231e3c3 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -350,7 +350,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindTypeOrAlias(ExpressionS private NamespaceOrTypeSymbol GetContainingNamespaceOrNonExtensionType(Symbol symbol) { if (symbol.ContainingNamespaceOrType() is { } containing - && containing is not TypeSymbol { IsExtension: true }) + && containing is not NamedTypeSymbol { IsExtension: true }) { return containing; } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 1f3ac35d1a2..574d0704b02 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -1261,7 +1261,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat } } - if (implementsInterface(enumeratorType, isAsync, diagnostics)) + if (implementsInterface(builder.CollectionType, enumeratorType, isAsync, diagnostics)) { builder.NeedsDisposal = true; return; @@ -1281,7 +1281,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat } } - bool implementsInterface(TypeSymbol enumeratorType, bool isAsync, BindingDiagnosticBag diagnostics) + bool implementsInterface(TypeSymbol collectionType, TypeSymbol enumeratorType, bool isAsync, BindingDiagnosticBag diagnostics) { CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); @@ -1295,7 +1295,8 @@ bool implementsInterface(TypeSymbol enumeratorType, bool isAsync, BindingDiagnos diagnostics.Add(syntax, useSiteInfo); if (needSupportForRefStructInterfaces && - enumeratorType.ContainingModule != Compilation.SourceModule) + enumeratorType.ContainingModule != Compilation.SourceModule && + !LocalRewriter.CanRewriteForEachAsFor(Compilation, syntax, collectionType, out _, out _, BindingDiagnosticBag.Discarded)) { CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); } @@ -1824,7 +1825,8 @@ private bool AllInterfacesContainsIEnumerable( } } - if (implementedIEnumerable is not null && needSupportForRefStructInterfaces && type.ContainingModule != Compilation.SourceModule) + if (implementedIEnumerable is not null && needSupportForRefStructInterfaces && type.ContainingModule != Compilation.SourceModule && + !LocalRewriter.CanRewriteForEachAsFor(Compilation, collectionSyntax, type, out _, out _, BindingDiagnosticBag.Discarded)) { CheckFeatureAvailability(collectionSyntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index d34dc9c50ac..6fa6ce711ab 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -757,12 +757,12 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO BeforeVisitingSkippedBoundCallChildren(node); - VisitReceiver(in methodInvocationInfo); + visitReceiver(node, in methodInvocationInfo); var nodeAndInvocationInfo = (call: node, methodInvocationInfo); do { - VisitArguments(nodeAndInvocationInfo.call, in nodeAndInvocationInfo.methodInvocationInfo); + visitArguments(nodeAndInvocationInfo.call, in nodeAndInvocationInfo.methodInvocationInfo); } while (calls.TryPop(out nodeAndInvocationInfo!)); @@ -770,8 +770,8 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO } else { - VisitReceiver(in methodInvocationInfo); - VisitArguments(node, in methodInvocationInfo); + visitReceiver(node, in methodInvocationInfo); + visitArguments(node, in methodInvocationInfo); } return null; @@ -779,9 +779,38 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO static MethodInvocationInfo getInvocationInfo(BoundCall node) { var methodInvocationInfo = MethodInvocationInfo.FromCall(node); - methodInvocationInfo = ReplaceWithExtensionImplementationIfNeeded(in methodInvocationInfo); + + if (!node.IsErroneousNode) + { + methodInvocationInfo = ReplaceWithExtensionImplementationIfNeeded(in methodInvocationInfo); + } + return methodInvocationInfo; } + + void visitReceiver(BoundCall node, ref readonly MethodInvocationInfo methodInvocationInfo) + { + if (node.IsErroneousNode) + { + Visit(node.ReceiverOpt); + } + else + { + VisitReceiver(in methodInvocationInfo); + } + } + + void visitArguments(BoundCall node, ref readonly MethodInvocationInfo methodInvocationInfo) + { + if (node.IsErroneousNode) + { + VisitList(node.Arguments); + } + else + { + VisitArguments(node, in methodInvocationInfo); + } + } } protected override void VisitReceiver(BoundCall node) @@ -1092,7 +1121,7 @@ private void VisitDeconstructionArguments(ArrayBuilder v return; } - if (invocation.Method is null) + if (invocation.IsErroneousNode || invocation.Method is null) { return; } @@ -1147,7 +1176,7 @@ private void VisitDeconstructionArguments(ArrayBuilder v { var (placeholder, placeholderConversion) = conversion.DeconstructConversionInfo[i]; var underlyingConversion = BoundNode.GetConversion(placeholderConversion, placeholder); - VisitDeconstructionArguments(nestedVariables, syntax, underlyingConversion, right: invocation.Arguments[i + offset]); + VisitDeconstructionArguments(nestedVariables, syntax, underlyingConversion, right: methodInvocationInfo.ArgsOpt[i + offset]); } } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index 471ca9f30bb..ab5afdaacdb 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -263,6 +263,7 @@ public bool Equals(MethodSymbol? x, MethodSymbol? y) var xExtension = x.OriginalDefinition.ContainingType; var xGroupingKey = ((SourceNamedTypeSymbol)xExtension).ExtensionGroupingName; + Debug.Assert(xGroupingKey is not null); var yExtension = y.OriginalDefinition.ContainingType; var yGroupingKey = ((SourceNamedTypeSymbol)yExtension).ExtensionGroupingName; @@ -294,6 +295,7 @@ public int GetHashCode(MethodSymbol op) var extension = op.OriginalDefinition.ContainingType; var groupingKey = ((SourceNamedTypeSymbol)extension).ExtensionGroupingName; + Debug.Assert(groupingKey is not null); result = Hash.Combine(result, groupingKey.GetHashCode()); foreach (var parameter in op.OriginalDefinition.AsMember(Normalize(extension)).Parameters) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index f49023620f8..1f201b57bd0 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -2676,7 +2676,7 @@ static bool isAcceptableRefMismatch(RefKind refKind, bool isInterpolatedStringHa private static RefKind GetParameterBetternessRefKind(ParameterSymbol parameter, TMember member) where TMember : Symbol { // For static extension members, the ref kind of the extension parameter shouldn't affect betterness. - bool isExtensionParameterOfStaticExtensionMember = parameter is { ContainingSymbol: TypeSymbol { IsExtension: true, ExtensionParameter: var extensionParameter } } + bool isExtensionParameterOfStaticExtensionMember = parameter is { ContainingSymbol: NamedTypeSymbol { IsExtension: true, ExtensionParameter: var extensionParameter } } && member.IsStatic && object.ReferenceEquals(parameter, extensionParameter); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs b/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs index 556e32c5621..fa09e8f2d01 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Binder/WithCrefTypeParametersBinder.cs @@ -63,6 +63,7 @@ private MultiDictionary CreateTypeParameterMap() case SyntaxKind.IndexerMemberCref: case SyntaxKind.OperatorMemberCref: case SyntaxKind.ConversionOperatorMemberCref: + case SyntaxKind.ExtensionMemberCref: { AddTypeParameters((MemberCrefSyntax)_crefSyntax, map); break; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs new file mode 100644 index 00000000000..4797afb7fe8 --- /dev/null +++ b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class BoundCall + { + public bool IsErroneousNode => ResultKind is not LookupResultKind.Viable; + + private partial void Validate() + { + Debug.Assert(ResultKind is not LookupResultKind.MemberGroup); + Debug.Assert(ResultKind is not LookupResultKind.StaticInstanceMismatch); + Debug.Assert(ResultKind is LookupResultKind.Viable || HasErrors); + + /* Tracking issue: https://github.com/dotnet/roslyn/issues/79426 + Debug.Assert(ResultKind is LookupResultKind.Viable || + new StackTrace(fNeedFileInfo: false).GetFrame(2)?.GetMethod() switch + { + { Name: nameof(ErrorCall), DeclaringType: { } declaringType } => declaringType == typeof(BoundCall), + { Name: nameof(Update), DeclaringType: { } declaringType } => declaringType == typeof(BoundCall), + _ => false + }); + */ + } + } +} diff --git a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 6900c90d9ce..457c95749ec 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1823,7 +1823,7 @@ - + @@ -1839,7 +1839,17 @@ + + + diff --git a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index 58b62bd2e52..630b3f27ffe 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -132,8 +132,15 @@ public static BoundCall ErrorCall( Binder binder) { if (!originalMethods.IsEmpty) + { resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); + } + else + { + Debug.Assert(method.OriginalDefinition is ErrorMethodSymbol); + } + Debug.Assert(resultKind is not LookupResultKind.Viable); Debug.Assert(arguments.IsDefaultOrEmpty || (object)receiverOpt != (object)arguments[0]); return new BoundCall( diff --git a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index b1fba6ed796..f259e53280d 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -106,16 +106,22 @@ internal BoundLambda WithInAnonymousFunctionConversion() public TypeWithAnnotations GetInferredReturnType(ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) { // Nullability (and conversions) are ignored. - return GetInferredReturnType(conversions: null, nullableState: null, ref useSiteInfo, out inferredFromFunctionType); + return GetInferredReturnType(conversions: null, nullableState: null, getterNullResilienceData: null, ref useSiteInfo, out inferredFromFunctionType); } /// /// Infer return type. If `nullableState` is non-null, nullability is also inferred and `NullableWalker.Analyze` /// uses that state to set the inferred nullability of variables in the enclosing scope. `conversions` is /// only needed when nullability is inferred. + /// + /// If 'getterNullResilienceData' is non-null, it is propagated down to the child analysis pass, + /// so that it does not attempt to infer the field's nullable annotation, while a parent pass is also attempting to infer that. /// - public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, NullableWalker.VariableState? nullableState, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) + public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, NullableWalker.VariableState? nullableState, NullableWalker.GetterNullResilienceData? getterNullResilienceData, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) { + // Cannot pass 'getterNullResilienceData' without also passing 'nullableState'. + Debug.Assert(getterNullResilienceData is null || nullableState is not null); + if (!InferredReturnType.UseSiteDiagnostics.IsEmpty) { useSiteInfo.AddDiagnostics(InferredReturnType.UseSiteDiagnostics); @@ -151,7 +157,8 @@ public TypeWithAnnotations GetInferredReturnType(ConversionsBase? conversions, N diagnostics, delegateInvokeMethodOpt: delegateType?.DelegateInvokeMethod, initialState: nullableState, - returnTypes); + returnTypes, + getterNullResilienceData); diagnostics.Free(); inferredReturnType = InferReturnType(returnTypes, node: this, Binder, delegateType, Symbol.IsAsync, conversions); returnTypes.Free(); @@ -386,6 +393,7 @@ protected override BoundNode VisitExpressionOrPatternWithoutStackGuard(BoundNode internal partial class UnboundLambda { private readonly NullableWalker.VariableState? _nullableState; + private readonly NullableWalker.GetterNullResilienceData? _getterNullResilienceData; public static UnboundLambda Create( CSharpSyntaxNode syntax, @@ -417,16 +425,17 @@ public static UnboundLambda Create( return lambda; } - private UnboundLambda(SyntaxNode syntax, UnboundLambdaState state, FunctionTypeSymbol? functionType, bool withDependencies, NullableWalker.VariableState? nullableState, bool hasErrors) : + private UnboundLambda(SyntaxNode syntax, UnboundLambdaState state, FunctionTypeSymbol? functionType, bool withDependencies, NullableWalker.VariableState? nullableState, NullableWalker.GetterNullResilienceData? getterNullResilienceData, bool hasErrors) : this(syntax, state, functionType, withDependencies, hasErrors) { this._nullableState = nullableState; + this._getterNullResilienceData = getterNullResilienceData; } - internal UnboundLambda WithNullableState(NullableWalker.VariableState nullableState) + internal UnboundLambda WithNullabilityInfo(NullableWalker.VariableState nullableState, NullableWalker.GetterNullResilienceData? getterNullResilienceData) { var data = Data.WithCaching(true); - var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, nullableState, HasErrors); + var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, nullableState, getterNullResilienceData, HasErrors); data.SetUnboundLambda(lambda); return lambda; } @@ -439,7 +448,7 @@ internal UnboundLambda WithNoCache() return this; } - var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, _nullableState, HasErrors); + var lambda = new UnboundLambda(Syntax, data, FunctionType, WithDependencies, _nullableState, _getterNullResilienceData, HasErrors); data.SetUnboundLambda(lambda); return lambda; } @@ -482,7 +491,7 @@ public Binder GetWithParametersBinder(LambdaSymbol lambdaSymbol, Binder binder) public int ParameterCount { get { return Data.ParameterCount; } } public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTypeSymbol delegateType, ref CompoundUseSiteInfo useSiteInfo, out bool inferredFromFunctionType) - => BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, ref useSiteInfo, out inferredFromFunctionType); + => BindForReturnTypeInference(delegateType).GetInferredReturnType(conversions, _nullableState, _getterNullResilienceData, ref useSiteInfo, out inferredFromFunctionType); public RefKind RefKind(int index) { return Data.RefKind(index); } public ScopedKind DeclaredScope(int index) { return Data.DeclaredScope(index); } @@ -843,7 +852,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType, bool inExpressionTr var lambdaParameters = lambdaSymbol.Parameters; ParameterHelpers.EnsureRefKindAttributesExist(compilation, lambdaParameters, diagnostics, modifyCompilation: false); - // Not emitting ParamCollectionAttribute/ParamArrayAttribute for lambdas + ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, lambdaParameters, diagnostics, modifyCompilation: false); if (returnType.HasType) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/roslyn/src/Compilers/CSharp/Portable/CSharpResources.resx index 9bce63c7031..6d4974fc380 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/roslyn/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3833,12 +3833,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi The 'await' operator may only be used in a query expression within the first collection expression of the initial 'from' clause or within the collection expression of a 'join' clause - - 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 method lacks 'await' operators and will run synchronously - Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. @@ -8194,6 +8188,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - 'MethodImplAttribute.Async' and 'async' are not localizable. + 'MethodImplAttribute.Async' and 'async' are not localizable. + + + '{0}' cannot be applied manually. - \ No newline at end of file + diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/roslyn/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 48a83ce9891..1144fcc43a9 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1399,7 +1399,7 @@ private NamedTypeSymbol GetDeclaredExtension(ExtensionBlockDeclarationSyntax ext var declarationSpan = extensionDeclaration.Span; foreach (var symbol in collection) { - if (symbol is TypeSymbol { IsExtension: true } && symbol.HasLocationContainedWithin(this.SyntaxTree, declarationSpan, out var wasZeroWidthMatch)) + if (symbol is NamedTypeSymbol { IsExtension: true } && symbol.HasLocationContainedWithin(this.SyntaxTree, declarationSpan, out var wasZeroWidthMatch)) { if (!wasZeroWidthMatch) return (NamedTypeSymbol)symbol; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs b/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs index ee26ea798f7..7ff548c3126 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs @@ -529,7 +529,7 @@ private void CheckBaseTypeCompliance(NamedTypeSymbol symbol) else { NamedTypeSymbol baseType = symbol.EnumUnderlyingType ?? symbol.BaseTypeNoUseSiteDiagnostics; // null for interfaces - System.Diagnostics.Debug.Assert((object)baseType != null || symbol.SpecialType == SpecialType.System_Object, "Only object has no base."); + System.Diagnostics.Debug.Assert((object)baseType != null || symbol.SpecialType == SpecialType.System_Object || symbol.IsExtension, "Only object or extension has no base."); if ((object)baseType != null && !IsCompliantType(baseType, symbol)) { // TODO: it would be nice to report this on the base type clause. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs b/src/roslyn/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs index 23684f1b8c5..b4deda1dd8f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs @@ -101,7 +101,7 @@ public static void WriteDocumentationCommentXml(CSharpCompilation compilation, s { var compiler = new DocumentationCommentCompiler(assemblyName ?? compilation.SourceAssembly.Name, compilation, writer, filterTree, filterSpanWithinTree, processIncludes: true, isForSingleSymbol: false, diagnostics: diagnostics, cancellationToken: cancellationToken); - compiler.Visit(compilation.SourceAssembly.GlobalNamespace); + compiler.Visit(compilation.SourceAssembly.SourceModule.GlobalNamespace); Debug.Assert(compiler._indentDepth == 0); writer?.Flush(); } @@ -254,16 +254,16 @@ public override void VisitNamedType(NamedTypeSymbol symbol) if (sawExtension) { - appendContainedExtensions(symbol); + appendContainedExtensions((SourceNamedTypeSymbol)symbol); } } return; - void appendContainedExtensions(NamedTypeSymbol containingType) + void appendContainedExtensions(SourceNamedTypeSymbol containingType) { Debug.Assert(!_isForSingleSymbol); - ExtensionGroupingInfo extensionGroupingInfo = ((SourceMemberContainerTypeSymbol)containingType).GetExtensionGroupingInfo(); + ExtensionGroupingInfo extensionGroupingInfo = containingType.GetExtensionGroupingInfo(); foreach (ImmutableArray extensions in extensionGroupingInfo.EnumerateMergedExtensionBlocks()) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs b/src/roslyn/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs index a7954d3b284..51748d69715 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/DocumentationComments/DocumentationCommentIDVisitor.PartVisitor.cs @@ -190,11 +190,8 @@ public override object VisitNamedType(NamedTypeSymbol symbol, StringBuilder buil // (and return type, for conversions) as constructed with its own type parameters. if (!_inParameterOrReturnType && TypeSymbol.Equals(symbol, symbol.ConstructedFrom, TypeCompareKind.AllIgnoreOptions)) { - if (!symbol.IsExtension) - { - builder.Append('`'); - builder.Append(symbol.Arity); - } + builder.Append('`'); + builder.Append(symbol.Arity); } else { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/roslyn/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 2ebf8aa2ac1..ae55575572c 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -590,14 +591,55 @@ private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentInd index = -1; } - foreach (var member in symbol.GetMembers()) + bool haveExtensions = false; + + foreach (var member in (symbol.IsNamespace ? symbol.GetMembers() : symbol.GetTypeMembers().Cast())) { var namespaceOrType = member as NamespaceOrTypeSymbol; - if ((object)namespaceOrType != null && - member is not NamedTypeSymbol { IsExtension: true }) // https://github.com/dotnet/roslyn/issues/78963 - This is a temporary handling, we should get grouping and marker types processed instead. + if ((object)namespaceOrType != null) + { + Debug.Assert(namespaceOrType is PENamespaceSymbol or PENamedTypeSymbol); + + if (namespaceOrType is NamedTypeSymbol { IsExtension: true }) + { + haveExtensions = true; + } + else + { + GetExportedTypes(namespaceOrType, index, builder); + } + } + } + + if (haveExtensions) + { + var seenGroupingTypes = PooledHashSet.GetInstance(); + var groupingTypes = ArrayBuilder.GetInstance(); + + foreach (var type in symbol.GetTypeMembers("")) + { + if (!type.IsExtension) + { + continue; + } + + var groupingType = ((PENamedTypeSymbol)type).ExtensionGroupingType; + if (seenGroupingTypes.Add(groupingType)) + { + groupingTypes.Add(groupingType); + } + } + + seenGroupingTypes.Free(); + groupingTypes.Sort((x, y) => x.MetadataToken.CompareTo(y.MetadataToken)); + Debug.Assert(!groupingTypes.IsEmpty); + + foreach (var groupingType in groupingTypes) { - GetExportedTypes(namespaceOrType, index, builder); + GetExportedTypes(groupingType, index, builder); } + + groupingTypes.Free(); } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 13a6dab0ee3..8ec23581e5a 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1024,7 +1024,7 @@ internal enum ErrorCode ERR_BadAwaitInQuery = 1995, ERR_BadAwaitInLock = 1996, ERR_TaskRetNoObjectRequired = 1997, - WRN_AsyncLacksAwaits = 1998, + // WRN_AsyncLacksAwaits = 1998, ERR_FileNotFound = 2001, WRN_FileAlreadyIncluded = 2002, //ERR_DuplicateResponseFile = 2003, @@ -2425,6 +2425,7 @@ internal enum ErrorCode ERR_UnsupportedFeatureInRuntimeAsync = 9328, ERR_ExtensionBlockCollision = 9329, ERR_MethodImplAttributeAsyncCannotBeUsed = 9330, + ERR_AttributeCannotBeAppliedManually = 9331, // 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/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 001fab8d88b..75f0ef998b9 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -411,7 +411,6 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_CallerLineNumberPreferredOverCallerFilePath: case ErrorCode.WRN_DelaySignButNoKey: case ErrorCode.WRN_UnimplementedCommandLineSwitch: - case ErrorCode.WRN_AsyncLacksAwaits: case ErrorCode.WRN_BadUILang: case ErrorCode.WRN_RefCultureMismatch: case ErrorCode.WRN_ConflictingMachineAssembly: @@ -1485,7 +1484,6 @@ or ErrorCode.ERR_BadAsyncLacksBody or ErrorCode.ERR_BadAwaitInQuery or ErrorCode.ERR_BadAwaitInLock or ErrorCode.ERR_TaskRetNoObjectRequired - or ErrorCode.WRN_AsyncLacksAwaits or ErrorCode.ERR_FileNotFound or ErrorCode.WRN_FileAlreadyIncluded or ErrorCode.ERR_NoFileSpec @@ -2536,6 +2534,7 @@ or ErrorCode.ERR_MemberNameSameAsExtendedType or ErrorCode.ERR_FeatureNotAvailableInVersion14 or ErrorCode.ERR_ExtensionBlockCollision or ErrorCode.ERR_MethodImplAttributeAsyncCannotBeUsed + or ErrorCode.ERR_AttributeCannotBeAppliedManually => false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index e6e11d2093a..4b835ee748f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -403,55 +403,6 @@ protected override ImmutableArray Scan(ref bool badRegion) return pendingReturns; } - protected override ImmutableArray RemoveReturns() - { - var result = base.RemoveReturns(); - - if (CurrentSymbol is MethodSymbol currentMethod && currentMethod.IsAsync && !currentMethod.IsImplicitlyDeclared) - { - var foundAwait = result.Any(static pending => HasAwait(pending)); - if (!foundAwait) - { - // If we're on a LambdaSymbol, then use its 'DiagnosticLocation'. That will be - // much better than using its 'Location' (which is the entire span of the lambda). - var diagnosticLocation = CurrentSymbol is LambdaSymbol lambda - ? lambda.DiagnosticLocation - : CurrentSymbol.GetFirstLocationOrNone(); - - Diagnostics.Add(ErrorCode.WRN_AsyncLacksAwaits, diagnosticLocation); - } - } - - return result; - } - - private static bool HasAwait(PendingBranch pending) - { - var pendingBranch = pending.Branch; - if (pendingBranch is null) - { - return false; - } - - BoundKind kind = pendingBranch.Kind; - switch (kind) - { - case BoundKind.AwaitExpression: - return true; - case BoundKind.UsingStatement: - var usingStatement = (BoundUsingStatement)pendingBranch; - return usingStatement.AwaitOpt != null; - case BoundKind.ForEachStatement: - var foreachStatement = (BoundForEachStatement)pendingBranch; - return foreachStatement.AwaitOpt != null; - case BoundKind.UsingLocalDeclarations: - var localDeclaration = (BoundUsingLocalDeclarations)pendingBranch; - return localDeclaration.AwaitOpt != null; - default: - return false; - } - } - // For purpose of definite assignment analysis, awaits create pending branches, so async usings and foreachs do too public sealed override bool AwaitUsingAndForeachAddsPendingBranch => true; @@ -594,7 +545,7 @@ public static void Analyze( compatDiagnostics.Free(); foreach (var diagnostic in strictDiagnostics.AsEnumerable()) { - // If it is a warning (e.g. WRN_AsyncLacksAwaits), or an error that would be reported by the compatible analysis, just report it. + // If it is a warning, or an error that would be reported by the compatible analysis, just report it. if (diagnostic.Severity != DiagnosticSeverity.Error || compatDiagnosticSet.Contains(diagnostic)) { diagnostics.Add(diagnostic); @@ -2634,6 +2585,8 @@ protected void CheckAssigned(BoundExpression expr, SyntaxNode node) #nullable enable private void MarkFieldsUsed(TypeSymbol type) { + type = type.OriginalDefinition; + switch (type.TypeKind) { case TypeKind.Array: diff --git a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 91ff699e3d3..b79b40a3580 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -43,6 +43,25 @@ internal NullableAnalysisData(int maxRecursionDepth = -1) } } + /// + /// Additional info for getter null resilience analysis. + /// When this value is passed down through 'Analyze()', it means we are in the process of inferring the nullable annotation of 'field'. + /// The 'assumedAnnotation' should be used as the field's nullable annotation for this analysis pass. + /// See https://github.com/dotnet/csharplang/blob/229406aa6dc51c1e37b98e90eb868d979ec6d195/proposals/csharp-14.0/field-keyword.md#nullability-of-the-backing-field + /// + /// Indicates whether the *not-annotated* pass or the *annotated* pass is being performed. + internal readonly struct GetterNullResilienceData(SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation) + { + public readonly SynthesizedBackingFieldSymbol field = field; + public readonly NullableAnnotation assumedAnnotation = assumedAnnotation; + + public void Deconstruct(out SynthesizedBackingFieldSymbol field, out NullableAnnotation assumedAnnotation) + { + field = this.field; + assumedAnnotation = this.assumedAnnotation; + } + } + /// /// Used to copy variable slots and types from the NullableWalker for the containing method /// or lambda to the NullableWalker created for a nested lambda or local function. @@ -184,7 +203,7 @@ internal string GetDebuggerDisplay() /// Non-null if we are performing the 'null-resilience' analysis of a getter which uses the 'field' keyword. /// In this case, the inferred nullable annotation of the backing field must not be used, as we are currently in the process of inferring it. /// - private readonly (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? _getterNullResilienceData; + private readonly GetterNullResilienceData? _getterNullResilienceData; /// /// If true, the parameter types and nullability from _delegateInvokeMethod is used for @@ -460,7 +479,7 @@ private NullableWalker( CSharpCompilation compilation, Symbol? symbol, bool useConstructorExitWarnings, - (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? getterNullResilienceData, + GetterNullResilienceData? getterNullResilienceData, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, @@ -1738,7 +1757,7 @@ internal static void AnalyzeIfNeeded( BoundNode node, SyntaxNode syntax, DiagnosticBag diagnostics, - (SourcePropertyAccessorSymbol getter, SynthesizedBackingFieldSymbol field, NullableAnnotation assumedNullableAnnotation)? getterNullResilienceData = null) + (SourcePropertyAccessorSymbol symbol, GetterNullResilienceData getterNullResilienceData)? symbolAndGetterNullResilienceData = null) { bool requiresAnalysis = true; var compilation = binder.Compilation; @@ -1754,13 +1773,13 @@ internal static void AnalyzeIfNeeded( Analyze( compilation, - symbol: getterNullResilienceData?.getter, + symbol: symbolAndGetterNullResilienceData?.symbol, node, binder, binder.Conversions, diagnostics, useConstructorExitWarnings: false, - getterNullResilienceData is var (_, field, annotation) ? (field, annotation) : null, + getterNullResilienceData: symbolAndGetterNullResilienceData?.getterNullResilienceData, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, delegateInvokeMethodOpt: null, @@ -1781,7 +1800,8 @@ internal static void Analyze( DiagnosticBag diagnostics, MethodSymbol? delegateInvokeMethodOpt, VariableState initialState, - ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt) + ArrayBuilder<(BoundReturnStatement, TypeWithAnnotations)>? returnTypesOpt, + GetterNullResilienceData? getterNullResilienceData) { var symbol = lambda.Symbol; var variables = Variables.Create(initialState.Variables).CreateNestedMethodScope(symbol); @@ -1790,7 +1810,7 @@ internal static void Analyze( compilation, symbol, useConstructorExitWarnings: false, - getterNullResilienceData: null, + getterNullResilienceData: getterNullResilienceData, useDelegateInvokeParameterTypes: useDelegateInvokeParameterTypes, useDelegateInvokeReturnType: useDelegateInvokeReturnType, delegateInvokeMethodOpt: delegateInvokeMethodOpt, @@ -1821,7 +1841,7 @@ private static void Analyze( Conversions conversions, DiagnosticBag diagnostics, bool useConstructorExitWarnings, - (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? getterNullResilienceData, + GetterNullResilienceData? getterNullResilienceData, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, @@ -5473,7 +5493,7 @@ [new BoundExpressionWithNullability(leftOperand.Syntax, leftOperand, leftUnderly derivedType = derivedType.StrippedType(); var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; var conversion = _conversions.ClassifyBuiltInConversion(derivedType, baseType, isChecked: false, ref discardedUseSiteInfo); - if (conversion.Exists && !conversion.IsExplicit) + if (conversion.Kind is ConversionKind.Identity or ConversionKind.ImplicitReference) { return derivedType; } @@ -6523,37 +6543,56 @@ private static BoundExpression CreatePlaceholderIfNecessary(BoundExpression expr VisitExpressionWithoutStackGuardEpilogue(receiver); // VisitExpressionWithoutStackGuard does this after visiting each node - bool isNewExtensionMethod = node.Method.GetIsNewExtensionMember(); - - // Only instance receivers go through VisitRvalue; arguments go through VisitArgumentEvaluate. - if (node.ReceiverOpt is not null && !isNewExtensionMethod) + if (node.IsErroneousNode) { VisitRvalueEpilogue(receiver); // VisitRvalue does this after visiting each node - receiverType = ResultType; - CheckCallReceiver(receiver, receiverType, node.Method); - extensionReceiverResult = null; + + if (node.ReceiverOpt is not null) + { + receiverType = ResultType; + extensionReceiverResult = null; + } + else + { + Debug.Assert(node.InvokedAsExtensionMethod); + extensionReceiverResult = _visitResult; + receiverType = default; + } } else { - // The receiver for new extension methods is analyzed as an argument - Debug.Assert(node.InvokedAsExtensionMethod || isNewExtensionMethod); + bool isNewExtensionMethod = node.Method.GetIsNewExtensionMember(); - var refKind = isNewExtensionMethod ? GetExtensionReceiverRefKind(node.Method) : GetRefKind(node.ArgumentRefKindsOpt, 0); - - FlowAnalysisAnnotations annotations; - if (isNewExtensionMethod) + // Only instance receivers go through VisitRvalue; arguments go through VisitArgumentEvaluate. + if (node.ReceiverOpt is not null && !isNewExtensionMethod) { - Debug.Assert(node.Method.ContainingType.ExtensionParameter is not null); - annotations = node.Method.ContainingType.ExtensionParameter.FlowAnalysisAnnotations; + VisitRvalueEpilogue(receiver); // VisitRvalue does this after visiting each node + receiverType = ResultType; + CheckCallReceiver(receiver, receiverType, node.Method); + extensionReceiverResult = null; } else { - TypeWithAnnotations paramsIterationType = default; - annotations = GetCorrespondingParameter(0, node.Method.Parameters, node.ArgsToParamsOpt, node.Expanded, ref paramsIterationType).Annotations; - } + // The receiver for new extension methods is analyzed as an argument + Debug.Assert(node.InvokedAsExtensionMethod || isNewExtensionMethod); + + var refKind = isNewExtensionMethod ? GetExtensionReceiverRefKind(node.Method) : GetRefKind(node.ArgumentRefKindsOpt, 0); + + FlowAnalysisAnnotations annotations; + if (isNewExtensionMethod) + { + Debug.Assert(node.Method.ContainingType.ExtensionParameter is not null); + annotations = node.Method.ContainingType.ExtensionParameter.FlowAnalysisAnnotations; + } + else + { + TypeWithAnnotations paramsIterationType = default; + annotations = GetCorrespondingParameter(0, node.Method.Parameters, node.ArgsToParamsOpt, node.Expanded, ref paramsIterationType).Annotations; + } - extensionReceiverResult = VisitArgumentEvaluateEpilogue(receiver, refKind, annotations); - receiverType = default; + extensionReceiverResult = VisitArgumentEvaluateEpilogue(receiver, refKind, annotations); + receiverType = default; + } } } @@ -6593,11 +6632,35 @@ bool tryGetReceiver(BoundCall node, [MaybeNullWhen(returnValue: false)] out Boun TypeWithState visitAndCheckReceiver(BoundCall node) { + if (node.IsErroneousNode) + { + return VisitBadExpressionChild(node.ReceiverOpt); + } + return VisitAndCheckReceiver(node.ReceiverOpt, node.Method); } void reinferMethodAndVisitArguments(BoundCall node, TypeWithState receiverType, VisitResult? firstArgumentResult = null) { + if (node.IsErroneousNode) + { + for (int i = 0; i < node.Arguments.Length; i++) + { + if (i == 0 && firstArgumentResult is { }) + { + continue; + } + + BoundExpression? child = node.Arguments[i]; + VisitBadExpressionChild(child); + } + + var type = TypeWithAnnotations.Create(node.Type); + SetLvalueResultType(node, type); + SetUpdatedSymbol(node, node.Method, node.Method); + return; + } + (MethodSymbol method, ImmutableArray results, bool returnNotNull) = ReInferMethodAndVisitArguments( node, node.ReceiverOpt, @@ -6609,7 +6672,6 @@ void reinferMethodAndVisitArguments(BoundCall node, TypeWithState receiverType, node.DefaultArguments, node.Expanded, node.InvokedAsExtensionMethod, - suppressAdjustmentForNewExtension: node.HasErrors, firstArgumentResult); LearnFromEqualsMethod(method, node, receiverType, results); @@ -6650,10 +6712,9 @@ private TypeWithState VisitAndCheckReceiver(BoundExpression? receiverOpt, Method BitVector defaultArguments, bool expanded, bool invokedAsExtensionMethod, - bool suppressAdjustmentForNewExtension, VisitResult? firstArgumentResult = null) { - bool adjustForNewExtension = method.GetIsNewExtensionMember() && !suppressAdjustmentForNewExtension; + bool adjustForNewExtension = method.GetIsNewExtensionMember(); refKindsOpt = GetArgumentRefKinds(refKindsOpt, adjustForNewExtension, method, arguments.Length); @@ -8493,7 +8554,7 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Visi // MethodTypeInferrer must infer nullability for lambdas based on the nullability // from flow analysis rather than the declared nullability. To allow that, we need // to re-bind lambdas in MethodTypeInferrer. - return getUnboundLambda((BoundLambda)argument, GetVariableState(_variables, lambdaState.Value)); + return getUnboundLambda((BoundLambda)argument, GetVariableState(_variables, lambdaState.Value), _getterNullResilienceData); } if (argument.Kind == BoundKind.CollectionExpression) @@ -8539,9 +8600,9 @@ BoundExpression getArgumentForMethodTypeInference(BoundExpression argument, Visi return new BoundExpressionWithNullability(argument.Syntax, argument, argumentType.NullableAnnotation, argumentType.Type); } - static UnboundLambda getUnboundLambda(BoundLambda expr, VariableState variableState) + static UnboundLambda getUnboundLambda(BoundLambda expr, VariableState variableState, GetterNullResilienceData? getterNullResilienceData) { - return expr.UnboundLambda.WithNullableState(variableState); + return expr.UnboundLambda.WithNullabilityInfo(variableState, getterNullResilienceData); } } @@ -10766,11 +10827,9 @@ private void VisitDeconstructMethodArguments(ArrayBuilder GetDeconstructionRightParts(BoundExpress argsToParamsOpt: default, defaultArguments: default, expanded: false, - invokedAsExtensionMethod: false, - suppressAdjustmentForNewExtension: false); + invokedAsExtensionMethod: false); if (node.Type.IsVoidType()) { @@ -11232,8 +11290,7 @@ private ImmutableArray GetDeconstructionRightParts(BoundExpress argsToParamsOpt: default, defaultArguments: default, expanded: false, - invokedAsExtensionMethod: false, - suppressAdjustmentForNewExtension: false); + invokedAsExtensionMethod: false); if (node.Type.IsVoidType()) { @@ -11703,7 +11760,6 @@ private void VisitForEachExpression( defaultArguments: enumeratorMethodInfo.DefaultArguments, expanded: enumeratorMethodInfo.Expanded, invokedAsExtensionMethod: true, - suppressAdjustmentForNewExtension: false, firstArgumentResult: _visitResult); targetTypeWithAnnotations = results[0].LValueType; @@ -11976,17 +12032,7 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) { foreach (var child in node.ChildBoundNodes) { - // https://github.com/dotnet/roslyn/issues/35042, we need to implement similar workarounds for object, collection, and dynamic initializers. - if (child is BoundLambda lambda) - { - TakeIncrementalSnapshot(lambda); - VisitLambda(lambda, delegateTypeOpt: null); - VisitRvalueEpilogue(lambda); - } - else - { - VisitRvalue(child as BoundExpression); - } + VisitBadExpressionChild(child); } var type = TypeWithAnnotations.Create(node.Type); @@ -11994,6 +12040,23 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) return null; } + private TypeWithState VisitBadExpressionChild(BoundExpression? child) + { + // https://github.com/dotnet/roslyn/issues/35042, we need to implement similar workarounds for object, collection, and dynamic initializers. + if (child is BoundLambda lambda) + { + TakeIncrementalSnapshot(lambda); + VisitLambda(lambda, delegateTypeOpt: null); + VisitRvalueEpilogue(lambda); + } + else + { + VisitRvalue(child as BoundExpression); + } + + return ResultType; + } + public override BoundNode? VisitTypeExpression(BoundTypeExpression node) { var result = base.VisitTypeExpression(node); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/roslyn/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 9281b156bf6..be4ae165bd7 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -6161,8 +6161,12 @@ public BoundCall(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState ini this.DefaultArguments = defaultArguments; this.ResultKind = resultKind; this.OriginalMethodsOpt = originalMethodsOpt; + Validate(); } + [Conditional("DEBUG")] + private partial void Validate(); + public new TypeSymbol Type => base.Type!; public BoundExpression? ReceiverOpt { get; } public ThreeState InitialBindingReceiverIsSubjectToCloning { get; } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/roslyn/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index 35a3f0182d2..34056cc7577 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -116,7 +116,6 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_MultipleRuntimeOverrideMatches: case ErrorCode.WRN_DynamicDispatchToConditionalMethod: case ErrorCode.WRN_IsDynamicIsConfusing: - case ErrorCode.WRN_AsyncLacksAwaits: case ErrorCode.WRN_FileAlreadyIncluded: case ErrorCode.WRN_NoSources: case ErrorCode.WRN_NoConfigNotOnCommandLine: diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs index 972b5f14079..1a38c2de037 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/RuntimeAsyncRewriter.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp; @@ -22,30 +24,78 @@ public static BoundStatement Rewrite( return node; } - // https://github.com/dotnet/roslyn/issues/79763: struct lifting var variablesToHoist = IteratorAndAsyncCaptureWalker.Analyze(compilationState.Compilation, method, node, isRuntimeAsync: true, diagnostics.DiagnosticBag); + var hoistedLocals = ArrayBuilder.GetInstance(); + var factory = new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics); + var rewriter = new RuntimeAsyncRewriter(factory, variablesToHoist, hoistedLocals); + var thisStore = hoistThisIfNeeded(rewriter); + var result = (BoundStatement)rewriter.Visit(node); - if (variablesToHoist.Count > 0) + if (thisStore is not null) { - foreach (var variable in variablesToHoist) - { - // Method '{0}' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - diagnostics.Add(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, variable.GetFirstLocation(), method); - } + result = factory.Block(hoistedLocals.ToImmutableAndFree(), + factory.HiddenSequencePoint(), + factory.ExpressionStatement(thisStore), + result); + } + else if (hoistedLocals.Count > 0) + { + result = factory.Block(hoistedLocals.ToImmutableAndFree(), result); + } + else + { + hoistedLocals.Free(); } - var rewriter = new RuntimeAsyncRewriter(new SyntheticBoundNodeFactory(method, node.Syntax, compilationState, diagnostics)); - var result = (BoundStatement)rewriter.Visit(node); return SpillSequenceSpiller.Rewrite(result, method, compilationState, diagnostics); + + static BoundAssignmentOperator? hoistThisIfNeeded(RuntimeAsyncRewriter rewriter) + { + Debug.Assert(rewriter._factory.CurrentFunction is not null); + var thisParameter = rewriter._factory.CurrentFunction.ThisParameter; + if (thisParameter is { Type.IsValueType: true, RefKind: not RefKind.None }) + { + // This is a struct or a type parameter. We need to replace it with a hoisted local to preserve behavior from + // compiler-generated state machines; `this` is a ref, but results are not observable outside of the method. + // We do this regardless of whether `this` is captured to a ref local, because any usage of `ldarg.0` in these + // scenarios is illegal after the first await. We could be more precise and only do this if `this` is actually + // used after the first await, but at the moment we don't feel that is worth the complexity. + var hoistedThis = rewriter._factory.StoreToTemp(rewriter._factory.This(), out BoundAssignmentOperator store, kind: SynthesizedLocalKind.AwaitByRefSpill); + rewriter._hoistedLocals.Add(hoistedThis.LocalSymbol); + rewriter._proxies.Add(thisParameter, new CapturedToExpressionSymbolReplacement(hoistedThis, hoistedSymbols: [], isReusable: true)); + return store; + } + + return null; + } } private readonly SyntheticBoundNodeFactory _factory; private readonly Dictionary _placeholderMap; + private readonly IReadOnlySet _variablesToHoist; + private readonly RefInitializationHoister _refInitializationHoister; + private readonly ArrayBuilder _hoistedLocals; + private readonly Dictionary _proxies = []; - private RuntimeAsyncRewriter(SyntheticBoundNodeFactory factory) + private RuntimeAsyncRewriter(SyntheticBoundNodeFactory factory, IReadOnlySet variablesToHoist, ArrayBuilder hoistedLocals) { + Debug.Assert(factory.CurrentFunction != null); _factory = factory; _placeholderMap = []; + _variablesToHoist = variablesToHoist; + _refInitializationHoister = new RefInitializationHoister(_factory, _factory.CurrentFunction, TypeMap.Empty); + _hoistedLocals = hoistedLocals; + } + + [return: NotNullIfNotNull(nameof(node))] + public override BoundNode? Visit(BoundNode? node) + { + if (node == null) return node; + var oldSyntax = _factory.Syntax; + _factory.Syntax = node.Syntax; + var result = base.Visit(node); + _factory.Syntax = oldSyntax; + return result; } [return: NotNullIfNotNull(nameof(node))] @@ -161,4 +211,117 @@ public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlac { return _placeholderMap[node]; } + + public override BoundNode? VisitAssignmentOperator(BoundAssignmentOperator node) + { + if (node.Left is not BoundLocal leftLocal) + { + return base.VisitAssignmentOperator(node); + } + + BoundExpression visitedRight; + + if (_variablesToHoist.Contains(leftLocal.LocalSymbol) && !_proxies.ContainsKey(leftLocal.LocalSymbol)) + { + Debug.Assert(leftLocal.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.Spill || + (leftLocal.LocalSymbol.SynthesizedKind == SynthesizedLocalKind.ForEachArray && leftLocal.LocalSymbol.Type.HasInlineArrayAttribute(out _) && leftLocal.LocalSymbol.Type.TryGetInlineArrayElementField() is object)); + Debug.Assert(node.IsRef); + visitedRight = VisitExpression(node.Right); + return _refInitializationHoister.HoistRefInitialization( + leftLocal.LocalSymbol, + visitedRight, + _proxies, + createHoistedLocal, + createHoistedAccess, + this, + isRuntimeAsync: true); + } + + var visitedLeftOrProxy = VisitExpression(leftLocal); + visitedRight = VisitExpression(node.Right); + + if (visitedLeftOrProxy is not BoundLocal visitLeftLocal) + { + // Proxy replacement occurred. We need to reassign the proxy into our local as a sequence. + // ref leftLocal = ref proxy; + // leftLocal = visitedRight; + var assignment = _factory.AssignmentExpression(leftLocal, visitedLeftOrProxy, isRef: true); + return _factory.Sequence([assignment], node.Update(leftLocal, visitedRight, node.IsRef, node.Type)); + } + + return node.Update(visitedLeftOrProxy, visitedRight, node.IsRef, node.Type); + + static LocalSymbol createHoistedLocal(TypeSymbol type, RuntimeAsyncRewriter @this, LocalSymbol local) + { + var hoistedLocal = @this._factory.SynthesizedLocal(type, syntax: local.GetDeclaratorSyntax(), kind: SynthesizedLocalKind.AwaitByRefSpill); + @this._hoistedLocals.Add(hoistedLocal); + return hoistedLocal; + } + + static BoundLocal createHoistedAccess(LocalSymbol local, RuntimeAsyncRewriter @this) + => @this._factory.Local(local); + } + + private bool TryReplaceWithProxy(Symbol localOrParameter, SyntaxNode syntax, [NotNullWhen(true)] out BoundNode? replacement) + { + if (_proxies.TryGetValue(localOrParameter, out CapturedSymbolReplacement? proxy)) + { + replacement = proxy.Replacement(syntax, makeFrame: null, this); + return true; + } + + replacement = null; + return false; + } + + public override BoundNode VisitLocal(BoundLocal node) + { + if (TryReplaceWithProxy(node.LocalSymbol, node.Syntax, out BoundNode? replacement)) + { + return replacement; + } + + Debug.Assert(!_variablesToHoist.Contains(node.LocalSymbol)); + return base.VisitLocal(node)!; + } + + public override BoundNode? VisitParameter(BoundParameter node) + { + if (TryReplaceWithProxy(node.ParameterSymbol, node.Syntax, out BoundNode? replacement)) + { + // Currently, the only parameter we expect to be replaced is `this`, which is handled through VisitThisReference. + // Any other ref to a parameter should have either already been hoisted to a local during local rewriting, or should + // be an illegal ref to a parameter across an await. + throw ExceptionUtilities.Unreachable(); + } + + Debug.Assert(!_variablesToHoist.Contains(node.ParameterSymbol)); + return base.VisitParameter(node); + } + + public override BoundNode? VisitThisReference(BoundThisReference node) + { + Debug.Assert(_factory.CurrentFunction is not null); + var thisParameter = this._factory.CurrentFunction.ThisParameter; + if (TryReplaceWithProxy(thisParameter, node.Syntax, out BoundNode? replacement)) + { + return replacement; + } + + Debug.Assert(thisParameter is not { Type.IsValueType: true, RefKind: RefKind.Ref }); + return base.VisitThisReference(node); + } + + public override BoundNode? VisitExpressionStatement(BoundExpressionStatement node) + { + var expr = VisitExpression(node.Expression); + if (expr is null) + { + // Happens when the node is a hoisted expression that has no side effects. + // The generated proxy will have the original content from this node and we can drop it. + return _factory.StatementList(); + } + + return node.Update(expr); + } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs index 07fd38f7fef..0ac7e0bfe8d 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs @@ -192,17 +192,50 @@ public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlac { return null; } + if (property.ContainingType.IsAnonymousType) { //at this point we expect that the code is lowered and that getters of anonymous types are accessed - //only via their corresponding get-methods (see VisitMethodSymbol) + //only via their corresponding get-methods, except for properties in expression trees + + // Property of an anonymous type + var newType = (NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly(); + if (ReferenceEquals(newType, property.ContainingType)) + { + // Anonymous type symbol was not rewritten + return property; + } + + // get a new property by name + foreach (var member in newType.GetMembers(property.Name)) + { + if (member.Kind == SymbolKind.Property) + { + return (PropertySymbol)member; + } + } + throw ExceptionUtilities.Unreachable(); } + return ((PropertySymbol)property.OriginalDefinition) .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly()) ; } + [return: NotNullIfNotNull(nameof(field))] + public override FieldSymbol? VisitFieldSymbol(FieldSymbol? field) + { + if (field is null) + { + return null; + } + + // Field of a regular type + return ((FieldSymbol)field.OriginalDefinition) + .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(field.ContainingType).AsTypeSymbolOnly()); + } + public override BoundNode? VisitMethodDefIndex(BoundMethodDefIndex node) { // Cannot replace a MethodDefIndex's Method/Type with a substituted symbol. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs index 106a3fa207c..5ce6f070424 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs @@ -122,7 +122,8 @@ private void EnsureAttributesExist(TypeCompilationState compilationState) } ParameterHelpers.EnsureRefKindAttributesExist(moduleBuilder, Parameters); - // Not emitting ParamCollectionAttribute/ParamArrayAttribute for these methods because it is not a SynthesizedDelegateInvokeMethod + + ParameterHelpers.EnsureParamCollectionAttributeExists(moduleBuilder, Parameters); if (moduleBuilder.Compilation.ShouldEmitNativeIntegerAttributes()) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs index 61c5f3ed28e..73573a03a76 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs @@ -166,18 +166,6 @@ protected override ImmutableArray VisitDeclaredLocalFunctions(Immu } } - [return: NotNullIfNotNull(nameof(symbol))] - public override FieldSymbol? VisitFieldSymbol(FieldSymbol? symbol) - { - if (symbol is null) - { - return null; - } - - return symbol.OriginalDefinition - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(symbol.ContainingType).AsTypeSymbolOnly()); - } - public override BoundNode? VisitCall(BoundCall node) { return ExtensionMethodReferenceRewriter.VisitCall(this, node); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 81d153ff7ad..f38f89b6e7a 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -1219,23 +1219,13 @@ private static ImmutableArray GetEffectiveArgumentRefKinds(ImmutableArr for (int i = 0; i < parameters.Length; i++) { var paramRefKind = parameters[i].RefKind; - if (paramRefKind is RefKind.In or RefKind.RefReadOnlyParameter) + var currentArgRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; + var effectiveArgRefKind = GetEffectiveRefKind(paramRefKind, currentArgRefKind, parameters[i].Type, comRefKindMismatchPossible: false); + + if (currentArgRefKind != effectiveArgRefKind) { - var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; fillRefKindsBuilder(argumentRefKindsOpt, parameters, ref refKindsBuilder); - refKindsBuilder[i] = argRefKind == RefKind.None ? RefKind.In : RefKindExtensions.StrictIn; - } - else if (paramRefKind == RefKind.Ref) - { - var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; - if (argRefKind == RefKind.None) - { - // Interpolated strings used as interpolated string handlers are allowed to match ref parameters without `ref` - Debug.Assert(parameters[i].Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true, IsValueType: true }); - - fillRefKindsBuilder(argumentRefKindsOpt, parameters, ref refKindsBuilder); - refKindsBuilder[i] = RefKind.Ref; - } + refKindsBuilder[i] = effectiveArgRefKind; } } @@ -1266,6 +1256,38 @@ static void fillRefKindsBuilder(ImmutableArray argumentRefKindsOpt, Imm } } + internal static RefKind GetEffectiveRefKind(RefKind paramRefKind, RefKind initialArgRefKind, TypeSymbol paramType, bool comRefKindMismatchPossible) + { + // Patch refKinds for arguments that match 'in' or 'ref readonly' parameters to have effective RefKind + // For the purpose of further analysis we will mark the arguments as - + // - In if was originally passed as None and matches an 'in' or 'ref readonly' parameter + // - StrictIn if was originally passed as In or Ref and matches an 'in' or 'ref readonly' parameter + // Here and in the layers after the lowering we only care about None/notNone differences for the arguments + // Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule. + if (paramRefKind is RefKind.In or RefKind.RefReadOnlyParameter) + { + Debug.Assert(initialArgRefKind is RefKind.None or RefKind.In or RefKind.Ref); + return initialArgRefKind == RefKind.None ? RefKind.In : RefKindExtensions.StrictIn; + } + else if (paramRefKind == RefKind.Ref && initialArgRefKind == RefKind.None) + { + // For interpolated string handlers, we allow struct handlers to be passed as ref without a `ref` + // keyword + if (paramType is NamedTypeSymbol { IsInterpolatedStringHandlerType: true, IsValueType: true }) + { + return RefKind.Ref; + } + else + { + // For complex call locations, it's possible that there's a com parameter that allows passing by ref without an explicit ref keyword. This + // is not handled at the local rewriter. + Debug.Assert(comRefKindMismatchPossible); + } + } + + return initialArgRefKind; + } + // temporariesBuilder will be null when factory is null. internal static bool CanSkipRewriting( ImmutableArray rewrittenArguments, @@ -1428,20 +1450,7 @@ private void BuildStoresToTemps( } arguments[p] = StoreArgumentToTempIfNecessary(forceLambdaSpilling, storesToTemps, argument, argRefKind, paramRefKind); - - // Patch refKinds for arguments that match 'in' or 'ref readonly' parameters to have effective RefKind - // For the purpose of further analysis we will mark the arguments as - - // - In if was originally passed as None and matches an 'in' or 'ref readonly' parameter - // - StrictIn if was originally passed as In or Ref and matches an 'in' or 'ref readonly' parameter - // Here and in the layers after the lowering we only care about None/notNone differences for the arguments - // Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule. - if (paramRefKind is RefKind.In or RefKind.RefReadOnlyParameter) - { - Debug.Assert(argRefKind is RefKind.None or RefKind.In or RefKind.Ref); - argRefKind = argRefKind == RefKind.None ? RefKind.In : RefKindExtensions.StrictIn; - } - - refKinds[p] = argRefKind; + refKinds[p] = GetEffectiveRefKind(paramRefKind, argRefKind, parameters[p].Type, comRefKindMismatchPossible: true); } return; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 91f162a64b4..0abfd9551fa 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -283,7 +283,7 @@ static bool IsNewExtensionMemberWithByValPossiblyStructReceiver(Symbol symbol) // If the property is static or if the receiver is of kind "Base" or "this", then we can just generate prop = prop + value if (receiverOpt == null || propertyOrEvent.IsStatic || !CanChangeValueBetweenReads(receiverOpt)) { - return receiverOpt; + return VisitExpression(receiverOpt); } Debug.Assert(receiverOpt.Kind != BoundKind.TypeExpression); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs index 44fe74506b1..6e2467f28d6 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs @@ -69,26 +69,31 @@ public override BoundNode VisitForEachStatement(BoundForEachStatement node) } private bool CanRewriteForEachAsFor(SyntaxNode forEachSyntax, TypeSymbol nodeExpressionType, [NotNullWhen(true)] out MethodSymbol? indexerGet, [NotNullWhen(true)] out MethodSymbol? lengthGet) + { + return CanRewriteForEachAsFor(_compilation, forEachSyntax, nodeExpressionType, out indexerGet, out lengthGet, _diagnostics); + } + + internal static bool CanRewriteForEachAsFor(CSharpCompilation compilation, SyntaxNode forEachSyntax, TypeSymbol nodeExpressionType, [NotNullWhen(true)] out MethodSymbol? indexerGet, [NotNullWhen(true)] out MethodSymbol? lengthGet, BindingDiagnosticBag diagnostics) { lengthGet = indexerGet = null; var origDefinition = nodeExpressionType.OriginalDefinition; if (origDefinition.SpecialType == SpecialType.System_String) { - lengthGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length); - indexerGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars); + lengthGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Length, compilation, diagnostics); + indexerGet = UnsafeGetSpecialTypeMethod(forEachSyntax, SpecialMember.System_String__Chars, compilation, diagnostics); } - else if ((object)origDefinition == this._compilation.GetWellKnownType(WellKnownType.System_Span_T)) + else if ((object)origDefinition == compilation.GetWellKnownType(WellKnownType.System_Span_T)) { var spanType = (NamedTypeSymbol)nodeExpressionType; - lengthGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_Span_T__get_Length, isOptional: true)?.SymbolAsMember(spanType); - indexerGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_Span_T__get_Item, isOptional: true)?.SymbolAsMember(spanType); + lengthGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_Span_T__get_Length, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); + indexerGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_Span_T__get_Item, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); } - else if ((object)origDefinition == this._compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T)) + else if ((object)origDefinition == compilation.GetWellKnownType(WellKnownType.System_ReadOnlySpan_T)) { var spanType = (NamedTypeSymbol)nodeExpressionType; - lengthGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_ReadOnlySpan_T__get_Length, isOptional: true)?.SymbolAsMember(spanType); - indexerGet = (MethodSymbol?)_factory.WellKnownMember(WellKnownMember.System_ReadOnlySpan_T__get_Item, isOptional: true)?.SymbolAsMember(spanType); + lengthGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_ReadOnlySpan_T__get_Length, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); + indexerGet = (MethodSymbol?)Binder.GetWellKnownTypeMember(compilation, WellKnownMember.System_ReadOnlySpan_T__get_Item, diagnostics, syntax: forEachSyntax, isOptional: true)?.SymbolAsMember(spanType); } return lengthGet is { } && indexerGet is { }; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index ec31dbca802..0cb989bc6d6 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -178,6 +178,7 @@ private MethodSymbol GetOrCreateBaseFunctionWrapper(MethodSymbol methodBeingWrap return wrapper; } + /// Any new usage of this method will need a similar update in private bool TryReplaceWithProxy(Symbol parameterOrLocal, SyntaxNode syntax, [NotNullWhen(true)] out BoundNode? replacement) { if (proxies.TryGetValue(parameterOrLocal, out CapturedSymbolReplacement? proxy)) @@ -349,48 +350,6 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE return node.Update(rewrittenArgument, method, node.IsExtensionMethod, node.WasTargetTyped, type); } - [return: NotNullIfNotNull(nameof(property))] - private new PropertySymbol? VisitPropertySymbol(PropertySymbol? property) - { - if (property is null) - { - return null; - } - - if (!property.ContainingType.IsAnonymousType) - { - // Property of a regular type - return ((PropertySymbol)property.OriginalDefinition) - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly()); - } - - // Method of an anonymous type - var newType = (NamedTypeSymbol)TypeMap.SubstituteType(property.ContainingType).AsTypeSymbolOnly(); - if (ReferenceEquals(newType, property.ContainingType)) - { - // Anonymous type symbol was not rewritten - return property; - } - - // get a new property by name - foreach (var member in newType.GetMembers(property.Name)) - { - if (member.Kind == SymbolKind.Property) - { - return (PropertySymbol)member; - } - } - - throw ExceptionUtilities.Unreachable(); - } - - private new FieldSymbol VisitFieldSymbol(FieldSymbol field) - { - // Property of a regular type - return ((FieldSymbol)field.OriginalDefinition) - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(field.ContainingType).AsTypeSymbolOnly()); - } - public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMember node) { ImmutableArray arguments = (ImmutableArray)this.VisitList(node.Arguments); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs index a78955a399c..f7c465b0913 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/CapturedSymbol.cs @@ -62,16 +62,17 @@ public override BoundExpression Replacement(SyntaxNode node, Func : CapturedSymbolReplacement + where THoistedSymbolType : Symbol { private readonly BoundExpression _replacement; - public readonly ImmutableArray HoistedFields; + public readonly ImmutableArray HoistedSymbols; - public CapturedToExpressionSymbolReplacement(BoundExpression replacement, ImmutableArray hoistedFields, bool isReusable) + public CapturedToExpressionSymbolReplacement(BoundExpression replacement, ImmutableArray hoistedSymbols, bool isReusable) : base(isReusable) { _replacement = replacement; - this.HoistedFields = hoistedFields; + this.HoistedSymbols = hoistedSymbols; } public override BoundExpression Replacement(SyntaxNode node, Func makeFrame, TArg arg) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs index 3671e459416..6c62a86c384 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/MethodToStateMachineRewriter.cs @@ -96,6 +96,8 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter // Instrumentation related bound nodes: protected BoundBlockInstrumentation? instrumentation; + private readonly RefInitializationHoister _refInitializationHoister; + // new: public MethodToStateMachineRewriter( SyntheticBoundNodeFactory F, @@ -161,6 +163,8 @@ public MethodToStateMachineRewriter( slotAllocatorOpt, firstState: FirstIncreasingResumableState, increasing: true); + + _refInitializationHoister = new RefInitializationHoister(F, OriginalMethod, TypeMap); } #nullable disable @@ -365,7 +369,7 @@ private BoundStatement PossibleIteratorScope(ImmutableArray locals, } else { - foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) + foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedSymbols) { AddVariableCleanup(variableCleanup, field); @@ -505,226 +509,6 @@ private void FreeReusableHoistedField(StateMachineFieldSymbol field) fields.Add(field); } - private BoundExpression HoistRefInitialization(LocalSymbol local, BoundAssignmentOperator node) - { - Debug.Assert( - local switch - { - TypeSubstitutedLocalSymbol tsl => tsl.UnderlyingLocalSymbol, - _ => local - } is SynthesizedLocal - ); - Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill || - (local.SynthesizedKind == SynthesizedLocalKind.ForEachArray && local.Type.HasInlineArrayAttribute(out _) && local.Type.TryGetInlineArrayElementField() is object)); - Debug.Assert(local.GetDeclaratorSyntax() != null); -#pragma warning disable format - Debug.Assert(local.SynthesizedKind switch - { - SynthesizedLocalKind.Spill => this.OriginalMethod.IsAsync, - SynthesizedLocalKind.ForEachArray => this.OriginalMethod.IsAsync || this.OriginalMethod.IsIterator, - _ => false - }); -#pragma warning restore format - - var right = (BoundExpression)Visit(node.Right); - - var sideEffects = ArrayBuilder.GetInstance(); - bool needsSacrificialEvaluation = false; - var hoistedFields = ArrayBuilder.GetInstance(); - - SyntaxNode awaitSyntaxOpt; - int syntaxOffset; - if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) - { - awaitSyntaxOpt = local.GetDeclaratorSyntax(); -#pragma warning disable format - Debug.Assert(local.SynthesizedKind switch - { - SynthesizedLocalKind.Spill => awaitSyntaxOpt.IsKind(SyntaxKind.AwaitExpression) || awaitSyntaxOpt.IsKind(SyntaxKind.SwitchExpression), - SynthesizedLocalKind.ForEachArray => awaitSyntaxOpt is CommonForEachStatementSyntax, - _ => false - }); -#pragma warning restore format - syntaxOffset = OriginalMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(awaitSyntaxOpt), awaitSyntaxOpt.SyntaxTree); - } - else - { - // These are only used to calculate debug id for ref-spilled variables, - // no need to do so in release build. - awaitSyntaxOpt = null; - syntaxOffset = -1; - } - - var replacement = HoistExpression(right, awaitSyntaxOpt, syntaxOffset, local.RefKind, sideEffects, hoistedFields, ref needsSacrificialEvaluation); - - proxies.Add(local, new CapturedToExpressionSymbolReplacement(replacement, hoistedFields.ToImmutableAndFree(), isReusable: true)); - - if (needsSacrificialEvaluation) - { - var type = TypeMap.SubstituteType(local.Type).Type; - var sacrificialTemp = F.SynthesizedLocal(type, refKind: RefKind.Ref); - Debug.Assert(TypeSymbol.Equals(type, replacement.Type, TypeCompareKind.ConsiderEverything2)); - return F.Sequence(ImmutableArray.Create(sacrificialTemp), sideEffects.ToImmutableAndFree(), F.AssignmentExpression(F.Local(sacrificialTemp), replacement, isRef: true)); - } - - if (sideEffects.Count == 0) - { - sideEffects.Free(); - return null; - } - - var last = sideEffects.Last(); - sideEffects.RemoveLast(); - return F.Sequence(ImmutableArray.Empty, sideEffects.ToImmutableAndFree(), last); - } - - private BoundExpression HoistExpression( - BoundExpression expr, - SyntaxNode awaitSyntaxOpt, - int syntaxOffset, - RefKind refKind, - ArrayBuilder sideEffects, - ArrayBuilder hoistedFields, - ref bool needsSacrificialEvaluation) - { - switch (expr.Kind) - { - case BoundKind.ArrayAccess: - { - var array = (BoundArrayAccess)expr; - BoundExpression expression = HoistExpression(array.Expression, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); - var indices = ArrayBuilder.GetInstance(); - foreach (var index in array.Indices) - { - indices.Add(HoistExpression(index, awaitSyntaxOpt, syntaxOffset, RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation)); - } - - needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions - return array.Update(expression, indices.ToImmutableAndFree(), array.Type); - } - - case BoundKind.FieldAccess: - { - var field = (BoundFieldAccess)expr; - if (field.FieldSymbol.IsStatic) - { - // the address of a static field, and the value of a readonly static field, is stable - if (refKind != RefKind.None || field.FieldSymbol.IsReadOnly) return expr; - goto default; - } - - if (refKind == RefKind.None) - { - goto default; - } - - var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; - - var receiver = HoistExpression(field.ReceiverOpt, awaitSyntaxOpt, syntaxOffset, - isFieldOfStruct ? refKind : RefKind.None, sideEffects, hoistedFields, ref needsSacrificialEvaluation); - if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) - { - needsSacrificialEvaluation = true; // need the null check in field receiver - } - - return F.Field(receiver, field.FieldSymbol); - } - - case BoundKind.ThisReference: - case BoundKind.BaseReference: - case BoundKind.DefaultExpression: - return expr; - - case BoundKind.Call: - var call = (BoundCall)expr; - // NOTE: There are two kinds of 'In' arguments that we may see at this point: - // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) - // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) - // - // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. - // The "strict" ones do not permit implicit copying, so the same situation should result in an error. - if (refKind != RefKind.None && refKind != RefKind.In) - { - Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.Out); - Debug.Assert(call.Method.RefKind != RefKind.None); - F.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, F.Syntax.Location, call.Method); - } - // method call is not referentially transparent, we can only spill the result value. - refKind = RefKind.None; - goto default; - - case BoundKind.ConditionalOperator: - var conditional = (BoundConditionalOperator)expr; - // NOTE: There are two kinds of 'In' arguments that we may see at this point: - // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) - // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) - // - // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. - // The "strict" ones do not permit implicit copying, so the same situation should result in an error. - if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) - { - Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.In); - Debug.Assert(conditional.IsRef); - F.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, F.Syntax.Location); - } - // conditional expr is not referentially transparent, we can only spill the result value. - refKind = RefKind.None; - goto default; - - default: - if (expr.ConstantValueOpt != null) - { - return expr; - } - - if (refKind != RefKind.None) - { - throw ExceptionUtilities.UnexpectedValue(expr.Kind); - } - - TypeSymbol fieldType = expr.Type; - StateMachineFieldSymbol hoistedField; - if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) - { - const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; - - Debug.Assert(awaitSyntaxOpt != null); - - int ordinal = _synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); - var id = new LocalDebugId(syntaxOffset, ordinal); - - // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. - // However, it may happen that the type changes, in which case we need to allocate a new slot. - int slotIndex; - if (slotAllocator == null || - !slotAllocator.TryGetPreviousHoistedLocalSlotIndex( - awaitSyntaxOpt, - F.ModuleBuilderOpt.Translate(fieldType, awaitSyntaxOpt, Diagnostics.DiagnosticBag), - kind, - id, - Diagnostics.DiagnosticBag, - out slotIndex)) - { - slotIndex = _nextFreeHoistedLocalSlot++; - } - - string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); - hoistedField = F.StateMachineField(expr.Type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); - _fieldsForCleanup.Add(hoistedField); - } - else - { - hoistedField = GetOrAllocateReusableHoistedField(fieldType, reused: out _); - } - - hoistedFields.Add(hoistedField); - - var replacement = F.Field(F.This(), hoistedField); - sideEffects.Add(F.AssignmentExpression(replacement, expr)); - return replacement; - } - } - #region Visitors public override BoundNode Visit(BoundNode node) @@ -863,7 +647,69 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) // We have an assignment to a variable that has not yet been assigned a proxy. // So we assign the proxy before translating the assignment. - return HoistRefInitialization(leftLocal, node); + var visitedRight = (BoundExpression)Visit(node.Right); + return _refInitializationHoister.HoistRefInitialization( + leftLocal, + visitedRight, + proxies, + createHoistedSymbol, + createHoistedAccess, + this, + isRuntimeAsync: false); + + static StateMachineFieldSymbol createHoistedSymbol(TypeSymbol type, MethodToStateMachineRewriter @this, LocalSymbol assignedLocal) + { + StateMachineFieldSymbol hoistedSymbol; + + // https://github.com/dotnet/roslyn/issues/79793 - consider whether runtime async will need some of this work for enc + if (@this.F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) + { + const SynthesizedLocalKind kind = SynthesizedLocalKind.AwaitByRefSpill; + SyntaxNode awaitSyntax = assignedLocal.GetDeclaratorSyntax(); +#pragma warning disable format + Debug.Assert(assignedLocal.SynthesizedKind switch + { + SynthesizedLocalKind.Spill => awaitSyntax.IsKind(SyntaxKind.AwaitExpression) || awaitSyntax.IsKind(SyntaxKind.SwitchExpression), + SynthesizedLocalKind.ForEachArray => awaitSyntax is CommonForEachStatementSyntax, + _ => false + }); +#pragma warning restore format + int syntaxOffset = @this.OriginalMethod.CalculateLocalSyntaxOffset(LambdaUtilities.GetDeclaratorPosition(awaitSyntax), awaitSyntax.SyntaxTree); + + Debug.Assert(awaitSyntax != null); + + int ordinal = @this._synthesizedLocalOrdinals.AssignLocalOrdinal(kind, syntaxOffset); + var id = new LocalDebugId(syntaxOffset, ordinal); + + // Editing await expression is not allowed. Thus all spilled fields will be present in the previous state machine. + // However, it may happen that the type changes, in which case we need to allocate a new slot. + int slotIndex; + if (@this.slotAllocator == null || + !@this.slotAllocator.TryGetPreviousHoistedLocalSlotIndex( + awaitSyntax, + @this.F.ModuleBuilderOpt.Translate(type, awaitSyntax, @this.Diagnostics.DiagnosticBag), + kind, + id, + @this.Diagnostics.DiagnosticBag, + out slotIndex)) + { + slotIndex = @this._nextFreeHoistedLocalSlot++; + } + + string fieldName = GeneratedNames.MakeHoistedLocalFieldName(kind, slotIndex); + hoistedSymbol = @this.F.StateMachineField(type, fieldName, new LocalSlotDebugInfo(kind, id), slotIndex); + @this._fieldsForCleanup.Add(hoistedSymbol); + } + else + { + hoistedSymbol = @this.GetOrAllocateReusableHoistedField(type, out _); + } + + return hoistedSymbol; + } + + static BoundFieldAccess createHoistedAccess(StateMachineFieldSymbol fieldSymbol, MethodToStateMachineRewriter @this) + => @this.F.Field(@this.F.This(), fieldSymbol); } /// diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs new file mode 100644 index 00000000000..47e165a8f0b --- /dev/null +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/RefInitializationHoister.cs @@ -0,0 +1,304 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp; + +/// +/// Helper used during state machine lowering (async / iterator) to "hoist" the initialization of +/// synthesized ref locals whose right-hand side may contain await or otherwise needs to be +/// evaluated before suspension points. +/// +/// A ref local whose initializer contains side effects (invocations, indexing, field dereference with +/// a non-trivial receiver, etc.) must be rewritten so that: +/// +/// All side effects are performed exactly once and in the original left-to-right order. +/// Potential exceptions (null dereference, bounds checks, etc.) occur before the first suspension (e.g. an await inside the method) just as they would have in the original code. +/// The resulting stored reference (the value of the ref local) remains stable across suspension (i.e. subsequent uses of the ref local after rewrites refer to a syntactically simple expression comprised only of the hoisted symbols and stable primitives). +/// +/// +/// +internal class RefInitializationHoister(SyntheticBoundNodeFactory f, MethodSymbol originalMethod, TypeMap typeMap) + where THoistedSymbol : Symbol + where THoistedAccess : BoundExpression +{ + private readonly SyntheticBoundNodeFactory _factory = f; + private readonly MethodSymbol _originalMethod = originalMethod; + private readonly TypeMap _typeMap = typeMap; + private bool _reportedError; + + /// + /// Hoists the right-hand side of a ref local (or similar synthesized local) initialization. + /// + /// Additional context type passed through to creation callbacks. + /// The synthesized local whose initialization is being processed. Must be of a supported synthesized kind. + /// The (already recursively visited by the enclosing rewriter) original right-hand side expression. + /// Dictionary receiving a proxy replacement mapping from to a stable expression assembled from hoisted components. + /// Factory that creates a new hoisted symbol for a sub-expression value of a given . + /// Factory that produces an access expression to a previously created hoisted symbol. + /// Additional context forwarded to the factories (typically state machine specific info). + /// True when performing lowered transformation for runtime-async (MoveNext-like) method body; affects some validation / debug assertions. + /// + /// A sequence expression containing the side-effect assignments (and possibly a sacrificial evaluation) + /// whose value is the final replacement expression used to initialize the ref local, or null + /// if no side effects needed hoisting. + /// + /// + /// On success a entry is added for the local. + /// Subsequent usages of the local are rewritten (elsewhere) to the replacement expression so the + /// state machine no longer needs to track the original local. + /// + internal BoundExpression? HoistRefInitialization( + LocalSymbol local, + BoundExpression visitedRight, + Dictionary proxies, + Func createHoistedSymbol, + Func createHoistedAccess, + TArg arg, + bool isRuntimeAsync) + { + Debug.Assert( + local switch + { + TypeSubstitutedLocalSymbol tsl => tsl.UnderlyingLocalSymbol, + _ => local + } is SynthesizedLocal + ); + Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill || + (local.SynthesizedKind == SynthesizedLocalKind.ForEachArray && local.Type.HasInlineArrayAttribute(out _) && local.Type.TryGetInlineArrayElementField() is object)); + Debug.Assert(local.GetDeclaratorSyntax() != null); +#pragma warning disable format + Debug.Assert(local.SynthesizedKind switch + { + SynthesizedLocalKind.Spill => this._originalMethod.IsAsync, + SynthesizedLocalKind.ForEachArray => this._originalMethod.IsAsync || this._originalMethod.IsIterator, + _ => false + }); +#pragma warning restore format + + var sideEffects = ArrayBuilder.GetInstance(); + bool needsSacrificialEvaluation = false; + var hoistedSymbols = ArrayBuilder.GetInstance(); + + var replacement = HoistExpression(visitedRight, local, local.RefKind, sideEffects, hoistedSymbols, ref needsSacrificialEvaluation, createHoistedSymbol, createHoistedAccess, arg, isRuntimeAsync, isFieldAccessOfStruct: false); + + proxies.Add(local, new CapturedToExpressionSymbolReplacement(replacement, hoistedSymbols.ToImmutableAndFree(), isReusable: true)); + + if (needsSacrificialEvaluation) + { + var type = _typeMap.SubstituteType(local.Type).Type; + var sacrificialTemp = _factory.SynthesizedLocal(type, refKind: RefKind.Ref); + Debug.Assert(TypeSymbol.Equals(type, replacement.Type, TypeCompareKind.ConsiderEverything2)); + return _factory.Sequence(ImmutableArray.Create(sacrificialTemp), sideEffects.ToImmutableAndFree(), _factory.AssignmentExpression(_factory.Local(sacrificialTemp), replacement, isRef: true)); + } + + if (sideEffects.Count == 0) + { + sideEffects.Free(); + return null; + } + + var last = sideEffects.Last(); + sideEffects.RemoveLast(); + return _factory.Sequence(ImmutableArray.Empty, sideEffects.ToImmutableAndFree(), last); + } + + /// + /// Recursively processes , hoisting side-effecting / non-stable sub-expressions into + /// separate symbols and returning a (syntactically) simpler expression tree composed only of: + /// + /// Original stable nodes (e.g. constants, this, static readonly field access when allowed). + /// Accesses to hoisted symbols created for previous sub-expressions. + /// + /// + /// Additional context argument type. + /// Expression to transform. + /// The local being initialized ultimately (used for diagnostics and to associate hoisted symbols). + /// The desired ref kind of the full expression result (propagated selectively when it impacts legality of hoisting). + /// Builder collecting assignment expressions that realize side effects exactly once. + /// Builder receiving the created hoisted symbols (parallel to order). + /// Flag that is set if the final composed expression must still be evaluated once now (to force exceptions / checks) even though the reference itself is stable. + /// Callback that creates a hoisted symbol. + /// Callback that creates an access to a hoisted symbol. + /// Additional callback context. + /// Indicates we are in runtime-async lowering path; changes certain debug-time invariants. + /// Whether the current expression is a field access whose receiver is a struct (important for reference preservation rules). + /// The replacement (side-effect free / stable) expression that can stand in for after the collected side effects execute. + private BoundExpression HoistExpression( + BoundExpression expr, + LocalSymbol assignedLocal, + RefKind refKind, + ArrayBuilder sideEffects, + ArrayBuilder hoistedSymbols, + ref bool needsSacrificialEvaluation, + Func createHoistedSymbol, + Func createHoistedAccess, + TArg arg, + bool isRuntimeAsync, + bool isFieldAccessOfStruct) + { + switch (expr.Kind) + { + case BoundKind.ArrayAccess: + { + var array = (BoundArrayAccess)expr; + BoundExpression expression = HoistExpression( + array.Expression, + assignedLocal, + refKind: RefKind.None, + sideEffects, + hoistedSymbols, + ref needsSacrificialEvaluation, + createHoistedSymbol, + createHoistedAccess, + arg, + isRuntimeAsync, + isFieldAccessOfStruct: false); + + var indices = ArrayBuilder.GetInstance(); + foreach (var index in array.Indices) + { + indices.Add(HoistExpression( + index, + assignedLocal, + refKind: RefKind.None, + sideEffects, + hoistedSymbols, + ref needsSacrificialEvaluation, + createHoistedSymbol, + createHoistedAccess, + arg, + isRuntimeAsync, + isFieldAccessOfStruct: false)); + } + + needsSacrificialEvaluation = true; // need to force array index out of bounds exceptions + return array.Update(expression, indices.ToImmutableAndFree(), array.Type); + } + + case BoundKind.FieldAccess: + { + var field = (BoundFieldAccess)expr; + if (field.FieldSymbol.IsStatic) + { + // the address of a static field, and the value of a readonly static field, is stable + if (refKind != RefKind.None || field.FieldSymbol.IsReadOnly) return expr; + goto default; + } + Debug.Assert(field.ReceiverOpt != null); + + if (refKind == RefKind.None) + { + goto default; + } + + var isFieldOfStruct = !field.FieldSymbol.ContainingType.IsReferenceType; + + var receiver = HoistExpression( + field.ReceiverOpt, + assignedLocal, + refKind: isFieldOfStruct ? refKind : RefKind.None, + sideEffects, + hoistedSymbols, + ref needsSacrificialEvaluation, + createHoistedSymbol, + createHoistedAccess, + arg, + isRuntimeAsync, + isFieldAccessOfStruct: isFieldOfStruct); + if (receiver.Kind != BoundKind.ThisReference && !isFieldOfStruct) + { + // Make sure that any potential NRE on the receiver happens before the await. + needsSacrificialEvaluation = true; + } + + return _factory.Field(receiver, field.FieldSymbol); + } + + case BoundKind.ThisReference: + case BoundKind.BaseReference: + case BoundKind.DefaultExpression: + return expr; + + case BoundKind.Call: + var call = (BoundCall)expr; + // NOTE: There are two kinds of 'In' arguments that we may see at this point: + // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) + // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) + // + // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. + // The "strict" ones do not permit implicit copying, so the same situation should result in an error. + if (refKind != RefKind.None && refKind != RefKind.In) + { + Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.Out); + if (call.Method.RefKind != RefKind.None) + { + _factory.Diagnostics.Add(ErrorCode.ERR_RefReturningCallAndAwait, _factory.Syntax.Location, call.Method); + _reportedError = true; + } + } + // method call is not referentially transparent, we can only spill the result value. + refKind = RefKind.None; + goto default; + + case BoundKind.ConditionalOperator: + var conditional = (BoundConditionalOperator)expr; + // NOTE: There are two kinds of 'In' arguments that we may see at this point: + // - `RefKindExtensions.StrictIn` (originally specified with 'in' modifier) + // - `RefKind.In` (specified with no modifiers and matched an 'in' or 'ref readonly' parameter) + // + // It is allowed to spill ordinary `In` arguments by value if reference-preserving spilling is not possible. + // The "strict" ones do not permit implicit copying, so the same situation should result in an error. + if (refKind != RefKind.None && refKind != RefKind.RefReadOnly) + { + Debug.Assert(refKind is RefKindExtensions.StrictIn or RefKind.Ref or RefKind.In); + Debug.Assert(conditional.IsRef); + _factory.Diagnostics.Add(ErrorCode.ERR_RefConditionalAndAwait, _factory.Syntax.Location); + _reportedError = true; + } + // conditional expr is not referentially transparent, we can only spill the result value. + refKind = RefKind.None; + goto default; + + default: + if (expr.ConstantValueOpt != null) + { + return expr; + } + + if (refKind != RefKind.None) + { + if (isRuntimeAsync) + { + // If an error was reported about ref escaping earlier, there could be illegal ref accesses later in the method, + // so we track that to ensure that we don't see unexpected cases here. + // This is an access to a field of a struct, or parameter or local of a type parameter, both of which happen by reference. + // The receiver should be a non-ref local or parameter. + // This is safe to hoist into a proxy as the original local will be accessed directly. + Debug.Assert(_reportedError || isFieldAccessOfStruct || expr.Type!.IsTypeParameter()); + Debug.Assert(_reportedError || expr is BoundLocal { LocalSymbol.RefKind: RefKind.None } + or BoundParameter { ParameterSymbol.RefKind: RefKind.None }); + } + else + { + throw ExceptionUtilities.UnexpectedValue(expr.Kind); + } + } + + Debug.Assert(expr.Type is not null); + var hoistedSymbol = createHoistedSymbol(expr.Type, arg, assignedLocal); + hoistedSymbols.Add(hoistedSymbol); + + var replacement = createHoistedAccess(hoistedSymbol, arg); + sideEffects.Add(_factory.AssignmentExpression(replacement, expr)); + return replacement; + } + } +} diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index 9e74f7c63b0..3f9f01b270e 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -129,7 +129,8 @@ private ImmutableArray MakeParameters() p.ExplicitDefaultConstantValue, // the synthesized parameter doesn't need to have the same ref custom modifiers as the base refCustomModifiers: default, - baseParameterForAttributes: inheritAttributes ? p : null)); + baseParameterForAttributes: inheritAttributes ? p : null, + isParams: this is SynthesizedClosureMethod && p.IsParams)); } var extraSynthed = ExtraSynthesizedRefParameters; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/roslyn/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 9e9cc965336..c8a7850c451 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -445,7 +445,7 @@ private IOperation CreateBoundCallOperation(BoundCall boundCall) ConstantValue? constantValue = boundCall.ConstantValueOpt; bool isImplicit = boundCall.WasCompilerGenerated; - if (!boundCall.OriginalMethodsOpt.IsDefault || IsMethodInvalid(boundCall.ResultKind, targetMethod)) + if (boundCall.IsErroneousNode) { ImmutableArray children = CreateFromArray(((IBoundInvalidNode)boundCall).InvalidNodeChildren); return new InvalidOperation(children, _semanticModel, syntax, type, constantValue, isImplicit); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Parser/AbstractLexer.cs b/src/roslyn/src/Compilers/CSharp/Portable/Parser/AbstractLexer.cs index 43006797668..9f21d1f8c01 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Parser/AbstractLexer.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Parser/AbstractLexer.cs @@ -76,11 +76,6 @@ protected void AddError(int position, int width, ErrorCode code, params object[] this.AddError(this.MakeError(position, width, code, args)); } - protected void AddError(int position, int width, XmlParseErrorCode code, params object[] args) - { - this.AddError(this.MakeError(position, width, code, args)); - } - protected void AddError(ErrorCode code) { this.AddError(MakeError(code)); @@ -126,12 +121,6 @@ protected SyntaxDiagnosticInfo MakeError(int position, int width, ErrorCode code return new SyntaxDiagnosticInfo(offset, width, code, args); } - protected XmlSyntaxDiagnosticInfo MakeError(int position, int width, XmlParseErrorCode code, params object[] args) - { - int offset = GetLexemeOffsetFromPosition(position); - return new XmlSyntaxDiagnosticInfo(offset, width, code, args); - } - private int GetLexemeOffsetFromPosition(int position) { return position >= LexemeStartPosition ? position - LexemeStartPosition : position; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs b/src/roslyn/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs index e5417a5f08f..877c3ee3f5b 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs @@ -812,10 +812,7 @@ protected override SyntaxDiagnosticInfo GetExpectedTokenError(SyntaxKind expecte // NOTE: There are no errors in crefs - only warnings. We accomplish this by wrapping every diagnostic in ErrorCode.WRN_ErrorOverride. if (InCref) { - int offset, width; - this.GetDiagnosticSpanForMissingToken(out offset, out width); - - return GetExpectedTokenError(expected, actual, offset, width); + return base.GetExpectedTokenError(expected, actual); } switch (expected) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 03c05482213..e20dbe93668 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -699,7 +699,15 @@ private void ParseNamespaceBodyWorker( if (!isGlobal || seen > NamespaceParts.GlobalAttributes) { RoslynDebug.Assert(attribute.Target != null, "Must have a target as IsPossibleGlobalAttributeDeclaration checks for that"); - attribute = this.AddError(attribute, attribute.Target.Identifier, ErrorCode.ERR_GlobalAttributesNotFirst); + + attribute = attribute.Update( + attribute.OpenBracketToken, + attribute.Target.Update( + this.AddError(attribute.Target.Identifier, ErrorCode.ERR_GlobalAttributesNotFirst), + attribute.Target.ColonToken), + attribute.Attributes, + attribute.CloseBracketToken); + this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, attribute); } else @@ -3241,7 +3249,9 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind private bool IsExtensionContainerStart() { - return this.CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword && IsFeatureEnabled(MessageID.IDS_FeatureExtensions); + // For error recovery, we recognize `extension` followed by `<` even in older language versions + return this.CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword && + (IsFeatureEnabled(MessageID.IDS_FeatureExtensions) || this.PeekToken(1).Kind == SyntaxKind.LessThanToken); } // if the modifiers do not contain async or replace and the type is the identifier "async" or "replace", then @@ -8313,7 +8323,7 @@ private bool IsPossibleLocalDeclarationStatement(bool isGlobalScriptLevel) return true; } - return IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(isGlobalScriptLevel); + return IsPossibleFirstTypedIdentifierInLocalDeclarationStatement(isGlobalScriptLevel); } private bool IsPossibleScopedKeyword(bool isFunctionPointerParameter) @@ -8322,7 +8332,7 @@ private bool IsPossibleScopedKeyword(bool isFunctionPointerParameter) return ParsePossibleScopedKeyword(isFunctionPointerParameter, isLambdaParameter: false) != null; } - private bool IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(bool isGlobalScriptLevel) + private bool IsPossibleFirstTypedIdentifierInLocalDeclarationStatement(bool isGlobalScriptLevel) { bool? typedIdentifier = IsPossibleTypedIdentifierStart(this.CurrentToken, this.PeekToken(1), allowThisKeyword: false); if (typedIdentifier != null) @@ -8394,11 +8404,22 @@ private bool IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(bool isGlo return true; } - if (st == ScanTypeFlags.NotType || this.CurrentToken.Kind != SyntaxKind.IdentifierToken) + if (st == ScanTypeFlags.NotType) { return false; } + if (this.CurrentToken.Kind != SyntaxKind.IdentifierToken) + { + // In the case of something like: + // List + // if + // we know that we're in an error case, as the following keyword must be the start of a new statement. + // We'd prefer to assume that this is an incomplete local declaration over an expression, as it's more likely + // the user is just in the middle of writing a local declaration, and not an expression. + return st == ScanTypeFlags.GenericTypeOrExpression && (IsDefiniteStatement() || IsTypeDeclarationStart() || IsAccessibilityModifier(CurrentToken.Kind)); + } + // T? and T* might start an expression, we need to parse further to disambiguate: if (isGlobalScriptLevel) { @@ -8457,7 +8478,7 @@ private bool IsPossibleTopLevelUsingLocalDeclarationStatement() EatToken(); } - return IsPossibleFirstTypedIdentifierInLocaDeclarationStatement(isGlobalScriptLevel: false); + return IsPossibleFirstTypedIdentifierInLocalDeclarationStatement(isGlobalScriptLevel: false); } // Looks ahead for a declaration of a field, property or method declaration following a nullable type T?. @@ -8905,17 +8926,16 @@ private PostSkipAction SkipBadStatementListTokens(SyntaxListBuilder(TNode node, int offset, int length, ErrorCode co return WithAdditionalDiagnostics(node, MakeError(offset, length, code, args)); } - protected TNode AddError(TNode node, CSharpSyntaxNode location, ErrorCode code, params object[] args) where TNode : CSharpSyntaxNode - { - // assumes non-terminals will at most appear once in sub-tree - int offset; - FindOffset(node, location, out offset); - return WithAdditionalDiagnostics(node, MakeError(offset, location.Width, code, args)); - } - protected TNode AddErrorToFirstToken(TNode node, ErrorCode code) where TNode : CSharpSyntaxNode { var firstToken = node.GetFirstToken(); @@ -849,14 +838,6 @@ protected TNode AddErrorToLastToken(TNode node, ErrorCode code) where TNo return WithAdditionalDiagnostics(node, MakeError(offset, width, code)); } - protected TNode AddErrorToLastToken(TNode node, ErrorCode code, params object[] args) where TNode : CSharpSyntaxNode - { - int offset; - int width; - GetOffsetAndWidthForLastToken(node, out offset, out width); - return WithAdditionalDiagnostics(node, MakeError(offset, width, code, args)); - } - private static void GetOffsetAndWidthForLastToken(TNode node, out int offset, out int width) where TNode : CSharpSyntaxNode { var lastToken = node.GetLastNonmissingToken(); @@ -1038,61 +1019,6 @@ internal SyntaxToken AddSkippedSyntax(SyntaxToken target, GreenNode skippedSynta return target; } - /// - /// This function searches for the given location node within the subtree rooted at root node. - /// If it finds it, the function computes the offset span of that child node within the root and returns true, - /// otherwise it returns false. - /// - /// Root node - /// Node to search in the subtree rooted at root node - /// Offset of the location node within the subtree rooted at child - /// - private bool FindOffset(GreenNode root, CSharpSyntaxNode location, out int offset) - { - int currentOffset = 0; - offset = 0; - if (root != null) - { - for (int i = 0, n = root.SlotCount; i < n; i++) - { - var child = root.GetSlot(i); - if (child == null) - { - // ignore null slots - continue; - } - - // check if the child node is the location node - if (child == location) - { - // Found the location node in the subtree - // Initialize offset with the offset of the location node within its parent - // and walk up the stack of recursive calls adding the offset of each node - // within its parent - offset = currentOffset; - return true; - } - - // search for the location node in the subtree rooted at child node - if (this.FindOffset(child, location, out offset)) - { - // Found the location node in child's subtree - // Add the offset of child node within its parent to offset - // and continue walking up the stack - offset += child.GetLeadingTriviaWidth() + currentOffset; - return true; - } - - // We didn't find the location node in the subtree rooted at child - // Move on to the next child - currentOffset += child.FullWidth; - } - } - - // We didn't find the location node within the subtree rooted at root node - return false; - } - protected static SyntaxToken ConvertToKeyword(SyntaxToken token) { if (token.Kind != token.ContextualKind) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs b/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs index 0164a90e51f..b40a295a573 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplay.cs @@ -279,8 +279,16 @@ private static ArrayBuilder PopulateDisplayParts( { var visitor = SymbolDisplayVisitor.GetInstance(builder, format, semanticModelOpt, positionOpt); symbol.Accept(visitor); + + if (symbol is INamedTypeSymbol { IsExtension: true } extension + && format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) + { + visitor.AddExtensionMarkerName(extension); + } + visitor.Free(); } + return builder; } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index 41f7aadad7c..fe0462cc6d6 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.SymbolDisplay; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -300,21 +299,18 @@ private void VisitNamedTypeWithoutNullability(INamedTypeSymbol symbol) if (IncludeNamedType(symbol.ContainingType)) { symbol.ContainingType.Accept(this.NotFirstVisitor); - - if (Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes)) - { - AddPunctuation(SyntaxKind.PlusToken); - } - else - { - AddPunctuation(SyntaxKind.DotToken); - } + AddNestedTypeSeparator(); } } AddNameAndTypeArgumentsOrParameters(symbol); } + private void AddNestedTypeSeparator() + { + AddPunctuation(Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes) ? SyntaxKind.PlusToken : SyntaxKind.DotToken); + } + private bool ShouldDisplayAsValueTuple(INamedTypeSymbol symbol) { Debug.Assert(symbol.IsTupleType); @@ -345,9 +341,7 @@ private void AddNameAndTypeArgumentsOrParameters(INamedTypeSymbol symbol) { if (Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) { - // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, should we output ExtensionMarkerName instead here? - var extensionIdentifier = underlyingTypeSymbol!.ExtensionGroupingName; // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, use public API once it's available - Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, symbol, extensionIdentifier)); + Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, symbol, symbol.ExtensionGroupingName)); } else { @@ -997,5 +991,12 @@ private void AddTypeParameterConstraints(ImmutableArray typeArgumen } } } + + internal void AddExtensionMarkerName(INamedTypeSymbol extension) + { + Debug.Assert(extension.IsExtension); + AddNestedTypeSeparator(); + Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, extension, extension.ExtensionMarkerName)); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs index e98d146e539..704d5ccba09 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs @@ -82,11 +82,9 @@ internal sealed override bool MangleName internal sealed override bool IsFileLocal => false; internal sealed override FileIdentifier? AssociatedFileIdentifier => null; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionMarkerName => null; public sealed override int Arity { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs index c478166ce91..ae62b4c0cc4 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs @@ -167,11 +167,9 @@ public sealed override bool IsRefLikeType get { return false; } } - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionMarkerName => null; public sealed override bool IsReadOnly { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs index 0ad5f45886c..b0c75572773 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs @@ -236,8 +236,6 @@ public override bool IsValueType } } - internal sealed override ParameterSymbol? ExtensionParameter => null; - internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => ManagedKind.Managed; public sealed override bool IsRefLikeType diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index 52fc241a7eb..69ff684f44c 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -30,6 +30,11 @@ internal abstract class AssemblySymbol : Symbol, IAssemblySymbolInternal // to the VB version. // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + /// + /// Separate pool for assembly symbols as these collections commonly exceed ArrayBuilder's size threshold. + /// + private static readonly ObjectPool> s_symbolPool = new ObjectPool>(() => new ArrayBuilder()); + /// /// The system assembly, which provides primitive types like Object, String, etc., e.g. mscorlib.dll. /// The value is provided by ReferenceManager and must not be modified. For SourceAssemblySymbol, non-missing @@ -948,7 +953,7 @@ internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type) Debug.Assert(this is SourceAssemblySymbol, "Never include references for a non-source assembly, because they don't know about aliases."); - var assemblies = ArrayBuilder.GetInstance(); + var assemblies = s_symbolPool.Allocate(); // ignore reference aliases if searching for a type from a specific assembly: if (assemblyOpt != null) @@ -1015,7 +1020,13 @@ internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type) result = candidate; } - assemblies.Free(); + assemblies.Clear(); + + // Do not call assemblies.Free, as the ArrayBuilder isn't associated with our pool and even if it were, we don't + // want the default freeing behavior of limiting pooled array size to ArrayBuilder.PooledArrayLengthLimitExclusive. + // Instead, we need to explicitly add this item back to our pool. + s_symbolPool.Free(assemblies); + Debug.Assert(result?.IsErrorType() != true); return result; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index bf51ba24015..8d4508fffce 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -537,9 +537,9 @@ internal void EnsureRequiresLocationAttributeExists(BindingDiagnosticBag? diagno EnsureEmbeddableAttributeExists(EmbeddableAttributes.RequiresLocationAttribute, diagnostics, location, modifyCompilation); } - internal void EnsureParamCollectionAttributeExistsAndModifyCompilation(BindingDiagnosticBag? diagnostics, Location location) + internal void EnsureParamCollectionAttributeExists(BindingDiagnosticBag? diagnostics, Location location, bool modifyCompilation) { - EnsureEmbeddableAttributeExists(EmbeddableAttributes.ParamCollectionAttribute, diagnostics, location, modifyCompilation: true); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.ParamCollectionAttribute, diagnostics, location, modifyCompilation: modifyCompilation); } internal void EnsureIsByRefLikeAttributeExists(BindingDiagnosticBag? diagnostics, Location location, bool modifyCompilation) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs index c88106e235d..48b5ecd9f21 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs @@ -107,8 +107,6 @@ public override bool IsValueType } } - internal sealed override ParameterSymbol? ExtensionParameter => null; - internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => ManagedKind.Managed; public sealed override bool IsRefLikeType diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs index ecddeb5e8d0..42a6283106b 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs @@ -116,11 +116,9 @@ public sealed override bool IsRefLikeType } } - internal sealed override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionGroupingName => null; - internal sealed override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionMarkerName => null; public sealed override bool IsReadOnly { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs index 12e793ae1ea..97841bb9571 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs @@ -73,7 +73,6 @@ private FunctionPointerTypeSymbol(FunctionPointerMethodSymbol signature) public override bool IsReferenceType => false; public override bool IsValueType => true; - internal sealed override ParameterSymbol? ExtensionParameter => null; public override TypeKind TypeKind => TypeKind.FunctionPointer; public override bool IsRefLikeType => false; public override bool IsReadOnly => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs index 1058dcf3aaa..0cbd87e3b03 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs @@ -99,8 +99,6 @@ internal void SetExpression(BoundExpression expression) public override bool IsValueType => false; - internal sealed override ParameterSymbol? ExtensionParameter => null; - public override TypeKind TypeKind => TypeKindInternal.FunctionType; public override bool IsRefLikeType => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index 224291f145a..39bc22054c6 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs @@ -95,12 +95,12 @@ internal static bool GetIsNewExtensionMember(this Symbol member) internal static bool GetIsNewExtensionMember(this MethodSymbol member) { - return member is { ContainingSymbol: TypeSymbol { IsExtension: true }, OriginalDefinition: not SynthesizedExtensionMarker }; + return member is { ContainingSymbol: NamedTypeSymbol { IsExtension: true }, OriginalDefinition: not SynthesizedExtensionMarker }; } internal static bool GetIsNewExtensionMember(this PropertySymbol member) { - return member.ContainingSymbol is TypeSymbol { IsExtension: true }; + return member.ContainingSymbol is NamedTypeSymbol { IsExtension: true }; } internal static bool TryGetInstanceExtensionParameter(this Symbol symbol, [NotNullWhen(true)] out ParameterSymbol? extensionParameter) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs index 044af626210..c57948af83e 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs @@ -486,6 +486,12 @@ internal void OnNewTypeDeclarationsLoaded( { foreach (var type in types) { + if (type.IsExtension) + { + // This symbol represents an extension block, not the marker type itself. + continue; + } + bool added; added = TypeHandleToTypeMap.TryAdd(type.Handle, type); Debug.Assert(added); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index f455e9eea85..8dd1dfdc522 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -1824,6 +1824,12 @@ private void EnsureNestedTypesAreLoaded() } } + private void SetExtensionGroupingTypeNestedTypes(ArrayBuilder groupingNestedTypes) + { + var exchangeResult = Interlocked.CompareExchange(ref _lazyNestedTypes, GroupByName(groupingNestedTypes), null); + Debug.Assert(exchangeResult == null); + } + public override ImmutableArray GetTypeMembers(ReadOnlyMemory name) { EnsureNestedTypesAreLoaded(); @@ -2221,9 +2227,12 @@ private IEnumerable CreateNestedTypes() yield break; } + var groupingNestedTypes = ArrayBuilder.GetInstance(); + foreach (var markerRid in markerTypeDefs) { var marker = PENamedTypeSymbol.Create(moduleSymbol, type, markerRid); + groupingNestedTypes.Add(marker); // Tracked by https://github.com/dotnet/roslyn/issues/78963 : test effect of every condition here if (marker.HasSpecialName && marker.IsStatic && marker.DeclaredAccessibility == Accessibility.Public && @@ -2240,6 +2249,9 @@ private IEnumerable CreateNestedTypes() } } + type.SetExtensionGroupingTypeNestedTypes(groupingNestedTypes); + groupingNestedTypes.Free(); + continue; } @@ -2686,20 +2698,11 @@ public override bool IsRefLikeType } } - internal override string ExtensionGroupingName - { - get - { - if (!IsExtension) - { - throw ExceptionUtilities.Unreachable(); - } - - return _lazyUncommonProperties.extensionInfo.GroupingTypeSymbol.MetadataName; - } - } +#nullable enable + internal override string? ExtensionGroupingName + => IsExtension ? _lazyUncommonProperties.extensionInfo.GroupingTypeSymbol.Name : null; - internal override string ExtensionMarkerName + internal PENamedTypeSymbol ExtensionGroupingType { get { @@ -2708,10 +2711,14 @@ internal override string ExtensionMarkerName throw ExceptionUtilities.Unreachable(); } - return MetadataName; + return _lazyUncommonProperties.extensionInfo.GroupingTypeSymbol; } } + internal override string? ExtensionMarkerName + => IsExtension ? _name : null; +#nullable disable + public override bool IsReadOnly { get diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index 630d6710d0e..d4b7ecb0977 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -904,7 +904,7 @@ private ImmutableArray DecodeInterpolatedStringHandlerArgumentAttribute() break; default: - if (ContainingSymbol is { IsStatic: false, ContainingSymbol: TypeSymbol { IsExtension: true, ExtensionParameter.Name: var extensionParameterName } } + if (ContainingSymbol is { IsStatic: false, ContainingSymbol: NamedTypeSymbol { IsExtension: true, ExtensionParameter.Name: var extensionParameterName } } && string.Equals(extensionParameterName, name, StringComparison.Ordinal)) { builder.Add(BoundInterpolatedStringArgumentPlaceholder.ExtensionReceiver); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 98ca1d3660c..572e34f243c 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -378,8 +379,7 @@ internal void DoGetExtensionMethods(ArrayBuilder methods, string n internal void GetExtensionContainers(ArrayBuilder extensions) { - // Consider whether IsClassType could be used instead. Tracked by https://github.com/dotnet/roslyn/issues/78275 - if (!IsReferenceType || !IsStatic || IsGenericType || !MightContainExtensionMethods) return; + if (!this.IsClassType() || !IsStatic || IsGenericType || !MightContainExtensionMethods) return; foreach (var nestedType in GetTypeMembersUnordered()) { @@ -522,16 +522,28 @@ public override string MetadataName /// internal abstract FileIdentifier? AssociatedFileIdentifier { get; } + [MemberNotNullWhen(true, nameof(ExtensionGroupingName), nameof(ExtensionMarkerName))] + public virtual bool IsExtension + => TypeKind == TypeKind.Extension; + + /// + /// For the type representing an extension declaration, returns the receiver parameter symbol. + /// It may be unnamed. + /// Note: this may be null even if is true, in error cases. + /// + internal abstract ParameterSymbol? ExtensionParameter { get; } + /// /// For extensions, returns the synthesized identifier for the grouping type. + /// Returns null otherwise. /// - internal abstract string ExtensionGroupingName { get; } + internal abstract string? ExtensionGroupingName { get; } /// /// For extensions, returns the synthesized identifier for the marker type. + /// Returns null otherwise. /// - internal abstract string ExtensionMarkerName { get; } - + internal abstract string? ExtensionMarkerName { get; } #nullable disable /// diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index a0394b2a13a..51fef886c20 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -300,11 +300,9 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui return false; } - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionMarkerName => null; private sealed class NativeIntegerTypeMap : AbstractTypeMap { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs index 8a19a720f6d..a4ebf6d2bf3 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs @@ -107,8 +107,6 @@ public override bool IsValueType } } - internal sealed override ParameterSymbol? ExtensionParameter => null; - internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => ManagedKind.Unmanaged; public sealed override bool IsRefLikeType diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs index a2dd62da743..09eb8d8bc5a 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/MethodSymbol.cs @@ -332,6 +332,21 @@ INamedTypeSymbol IMethodSymbol.AssociatedAnonymousDelegate DllImportData IMethodSymbol.GetDllImportData() => _underlying.GetDllImportData(); +#nullable enable + IMethodSymbol? IMethodSymbol.AssociatedExtensionImplementation + { + get + { + if (!_underlying.IsDefinition || !_underlying.GetIsNewExtensionMember()) + { + return null; + } + + return _underlying.TryGetCorrespondingExtensionImplementationMethod().GetPublicSymbol(); + } + } +#nullable disable + #region ISymbol Members protected override void Accept(SymbolVisitor visitor) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs index 38863cab17c..6d0a697c23b 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/NamedTypeSymbol.cs @@ -202,6 +202,26 @@ UnderlyingNamedTypeSymbol.OriginalDefinition is SourceMemberContainerTypeSymbol INamedTypeSymbol INamedTypeSymbol.NativeIntegerUnderlyingType => UnderlyingNamedTypeSymbol.NativeIntegerUnderlyingType.GetPublicSymbol(); +#nullable enable + bool INamedTypeSymbol.IsExtension + { + get + { + bool isExtension = UnderlyingNamedTypeSymbol.IsExtension; + + Debug.Assert(!isExtension + || (!string.IsNullOrEmpty(UnderlyingNamedTypeSymbol.ExtensionGroupingName) && !string.IsNullOrEmpty(UnderlyingNamedTypeSymbol.ExtensionMarkerName))); + + return isExtension; + } + } + + string? INamedTypeSymbol.ExtensionGroupingName => UnderlyingNamedTypeSymbol.ExtensionGroupingName; + string? INamedTypeSymbol.ExtensionMarkerName => UnderlyingNamedTypeSymbol.ExtensionMarkerName; + + IParameterSymbol? INamedTypeSymbol.ExtensionParameter => UnderlyingNamedTypeSymbol.ExtensionParameter?.GetPublicSymbol(); +#nullable disable + #region ISymbol Members protected sealed override void Accept(SymbolVisitor visitor) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs index 3bd1bf00024..ab1ab59e1ad 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs @@ -28,7 +28,7 @@ protected TypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) ITypeSymbol ITypeSymbol.WithNullableAnnotation(CodeAnalysis.NullableAnnotation nullableAnnotation) { - if (UnderlyingTypeSymbol.IsExtension) + if (UnderlyingTypeSymbol is Symbols.NamedTypeSymbol { IsExtension: true }) { throw new System.NotSupportedException(); } @@ -157,9 +157,17 @@ TypeKind ITypeSymbol.TypeKind bool ITypeSymbol.IsNativeIntegerType => UnderlyingTypeSymbol.IsNativeIntegerType; #nullable enable - bool ITypeSymbol.IsExtension => UnderlyingTypeSymbol.IsExtension; + bool ITypeSymbol.IsExtension => UnderlyingTypeSymbol is Symbols.NamedTypeSymbol { IsExtension: true }; - IParameterSymbol? ITypeSymbol.ExtensionParameter => UnderlyingTypeSymbol.ExtensionParameter?.GetPublicSymbol(); + IParameterSymbol? ITypeSymbol.ExtensionParameter + { + get + { + return UnderlyingTypeSymbol is Symbols.NamedTypeSymbol namedType + ? namedType.ExtensionParameter?.GetPublicSymbol() + : null; + } + } #nullable disable string ITypeSymbol.ToDisplayString(CodeAnalysis.NullableFlowState topLevelNullability, SymbolDisplayFormat format) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs index f6991c9c38a..97c9290ffe4 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs @@ -472,10 +472,10 @@ internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? bui internal override bool HasCompilerLoweringPreserveAttribute => _underlyingType.HasCompilerLoweringPreserveAttribute; - internal override string ExtensionGroupingName + internal override string? ExtensionGroupingName => _underlyingType.ExtensionGroupingName; - internal override string ExtensionMarkerName + internal override string? ExtensionMarkerName => _underlyingType.ExtensionMarkerName; } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs index 261e51378d2..cbf3f033207 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ExtensionGroupingInfo.cs @@ -38,6 +38,7 @@ public ExtensionGroupingInfo(SourceMemberContainerTypeSymbol container) } var sourceNamedType = (SourceNamedTypeSymbol)type; + Debug.Assert(sourceNamedType.ExtensionGroupingName is not null); var groupingMetadataName = sourceNamedType.ExtensionGroupingName; MultiDictionary? markerMap; @@ -48,6 +49,7 @@ public ExtensionGroupingInfo(SourceMemberContainerTypeSymbol container) groupingMap.Add(groupingMetadataName, markerMap); } + Debug.Assert(sourceNamedType.ExtensionMarkerName is not null); markerMap.Add(sourceNamedType.ExtensionMarkerName, sourceNamedType); } @@ -537,7 +539,9 @@ private abstract class ExtensionGroupingOrMarkerType : Cci.INestedTypeDefinition bool IDefinition.IsEncDeleted => false; - bool INamedTypeReference.MangleName => false; + bool INamedTypeReference.MangleName => MangleName; + + protected abstract bool MangleName { get; } string? INamedTypeReference.AssociatedFileIdentifier => null; @@ -759,6 +763,8 @@ protected override IEnumerable GetMethods(EmitContext context protected override IEnumerable NestedTypes => ExtensionMarkerTypes; + protected override bool MangleName => GenericParameterCount != 0; + protected override IEnumerable GetProperties(EmitContext context) { foreach (var marker in ExtensionMarkerTypes) @@ -898,6 +904,8 @@ protected override IEnumerable GetMethods(EmitContext context protected override IEnumerable NestedTypes => SpecializedCollections.EmptyEnumerable(); + protected override bool MangleName => false; + protected override IEnumerable GetProperties(EmitContext context) => SpecializedCollections.EmptyEnumerable(); protected override IEnumerable GetAttributes(EmitContext context) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs index 458f3456343..71cf1826fe2 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs @@ -195,10 +195,8 @@ internal sealed override bool HasCollectionBuilderAttribute(out TypeSymbol? buil return false; } - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionMarkerName => null; } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 8d0d5e2afa5..08ee6f078c9 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -126,7 +126,7 @@ internal void GetDeclarationDiagnostics(BindingDiagnosticBag addTo) var compilation = DeclaringCompilation; ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, addTo, modifyCompilation: false); - // Not emitting ParamCollectionAttribute/ParamArrayAttribute for local functions + ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, Parameters, addTo, modifyCompilation: false); ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, addTo, modifyCompilation: false); ParameterHelpers.EnsureScopedRefAttributeExists(compilation, Parameters, addTo, modifyCompilation: false); ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, addTo, modifyCompilation: false); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 68f7f3719b9..f5d244f233d 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -370,11 +370,19 @@ private static void EnsureRefKindAttributesExist(CSharpCompilation compilation, } } - internal static void EnsureParamCollectionAttributeExistsAndModifyCompilation(CSharpCompilation compilation, ImmutableArray parameters, BindingDiagnosticBag diagnostics) + internal static void EnsureParamCollectionAttributeExists(PEModuleBuilder moduleBuilder, ImmutableArray parameters) { if (parameters.LastOrDefault(static (p) => p.IsParamsCollection) is { } parameter) { - compilation.EnsureParamCollectionAttributeExistsAndModifyCompilation(diagnostics, GetParameterLocation(parameter)); + moduleBuilder.EnsureParamCollectionAttributeExists(null, null); + } + } + + internal static void EnsureParamCollectionAttributeExists(CSharpCompilation compilation, ImmutableArray parameters, BindingDiagnosticBag diagnostics, bool modifyCompilation) + { + if (parameters.LastOrDefault(static (p) => p.IsParamsCollection) is { } parameter) + { + compilation.EnsureParamCollectionAttributeExists(diagnostics, GetParameterLocation(parameter), modifyCompilation); } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs index 4e71714542d..01254ba3f24 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs @@ -47,8 +47,16 @@ private SourceConstructorSymbol( if (syntax.Identifier.ValueText != containingType.Name) { - // This is probably a method declaration with the type missing. - diagnostics.Add(ErrorCode.ERR_MemberNeedsType, location); + if (syntax.Identifier.Text == "extension") + { + bool reported = !MessageID.IDS_FeatureExtensions.CheckFeatureAvailability(diagnostics, syntax); + Debug.Assert(reported); + } + else + { + // This is probably a method declaration with the type missing. + diagnostics.Add(ErrorCode.ERR_MemberNeedsType, location); + } } bool hasAnyBody = syntax.HasAnyBody(); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index b90a8cc9d70..05406f282aa 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -89,7 +89,7 @@ internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conve var compilation = DeclaringCompilation; ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); - ParameterHelpers.EnsureParamCollectionAttributeExistsAndModifyCompilation(compilation, Parameters, diagnostics); + ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); ParameterHelpers.EnsureScopedRefAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs index 397c3b6d232..181aef159ba 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceDelegateMethodSymbol.cs @@ -323,7 +323,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, } ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); - ParameterHelpers.EnsureParamCollectionAttributeExistsAndModifyCompilation(compilation, Parameters, diagnostics); + ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(ReturnType)) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index f9cb27bf315..939b016395e 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -1843,6 +1843,8 @@ internal override IEnumerable GetInstanceFieldsAndEvents() protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) { var compilation = DeclaringCompilation; + var location = GetFirstLocation(); + if (IsInterface) { CheckInterfaceMembers(this.GetMembersAndInitializers().NonTypeMembers, diagnostics); @@ -1850,6 +1852,7 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) else if (IsExtension) { CheckExtensionMembers(this.GetMembers(), diagnostics); + MessageID.IDS_FeatureExtensions.CheckFeatureAvailability(diagnostics, compilation, location); } CheckMemberNamesDistinctFromType(diagnostics); @@ -1869,8 +1872,6 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) ReportRequiredMembers(diagnostics); } - var location = GetFirstLocation(); - if (this.IsRefLikeType) { compilation.EnsureIsByRefLikeAttributeExists(diagnostics, location, modifyCompilation: true); @@ -2415,7 +2416,7 @@ void checkMemberNameConflicts( void checkMemberNameConflictsInExtensions(BindingDiagnosticBag diagnostics) { - IEnumerable> extensionsByReceiverType = GetTypeMembers("").Where(static t => t.IsExtension).GroupBy(static t => ((SourceNamedTypeSymbol)t).ExtensionGroupingName); + IEnumerable> extensionsByReceiverType = GetTypeMembers("").Where(static t => t.IsExtension).GroupBy(static t => ((SourceNamedTypeSymbol)t).ExtensionGroupingName!); foreach (var grouping in extensionsByReceiverType) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index e3d9b05248f..239f79f1745 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -1756,18 +1756,22 @@ internal override System.Reflection.MethodImplAttributes ImplementationAttribute result |= (System.Reflection.MethodImplAttributes.Runtime | System.Reflection.MethodImplAttributes.InternalCall); } - if (this.IsAsync && this.DeclaringCompilation.IsRuntimeAsyncEnabledIn(this)) - { - // https://github.com/dotnet/roslyn/issues/79792: Use real value from MethodImplAttributes when available - // When a method is emitted using runtime async, we add MethodImplAttributes.Async to indicate to the - // runtime to generate the state machine - result |= (System.Reflection.MethodImplAttributes)0x2000; - } + AddAsyncImplAttributeIfNeeded(ref result); return result; } } + protected void AddAsyncImplAttributeIfNeeded(ref System.Reflection.MethodImplAttributes result) + { + if (this.IsAsync && this.DeclaringCompilation.IsRuntimeAsyncEnabledIn(this)) + { + // When a method is emitted using runtime async, we add MethodImplAttributes.Async to indicate to the + // runtime to generate the state machine + result |= System.Reflection.MethodImplAttributes.Async; + } + } + internal override int TryGetOverloadResolutionPriority() => GetEarlyDecodedWellKnownAttributeData()?.OverloadResolutionPriority ?? 0; } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 5b1badf54e3..d563bba112b 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -1826,9 +1826,13 @@ public override string MetadataName { get { - return IsExtension - ? ExtensionMarkerName - : base.MetadataName; + if (IsExtension) + { + Debug.Assert(ExtensionMarkerName is not null); + return ExtensionMarkerName; + } + + return base.MetadataName; } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs index 1cbd51b0eeb..b48b1ad7999 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs @@ -1095,11 +1095,15 @@ internal sealed override ParameterSymbol? ExtensionParameter } } - internal override string ExtensionGroupingName + internal override string? ExtensionGroupingName { get { - Debug.Assert(IsExtension); + if (!IsExtension) + { + return null; + } + if (_lazyExtensionInfo is null) { Interlocked.CompareExchange(ref _lazyExtensionInfo, new ExtensionInfo(), null); @@ -1114,11 +1118,15 @@ internal override string ExtensionGroupingName } } - internal override string ExtensionMarkerName + internal override string? ExtensionMarkerName { get { - Debug.Assert(IsExtension); + if (!IsExtension) + { + return null; + } + if (_lazyExtensionInfo is null) { Interlocked.CompareExchange(ref _lazyExtensionInfo, new ExtensionInfo(), null); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs index d90d172bb8e..d970039e679 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs @@ -245,7 +245,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, } ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); - ParameterHelpers.EnsureParamCollectionAttributeExistsAndModifyCompilation(compilation, Parameters, diagnostics); + ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(ReturnType)) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 6c2221f3afb..a012eec3400 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -108,7 +108,7 @@ protected SourceParameterSymbol( Location location) : base(owner, ordinal) { - Debug.Assert((owner.Kind == SymbolKind.Method) || (owner.Kind == SymbolKind.Property) || owner is TypeSymbol { IsExtension: true }); + Debug.Assert((owner.Kind == SymbolKind.Method) || (owner.Kind == SymbolKind.Property) || owner is NamedTypeSymbol { IsExtension: true }); _refKind = refKind; _scope = scope; _name = name; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index f5ac6800e9b..5debe9e38b0 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -1044,7 +1044,7 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, } ParameterHelpers.EnsureRefKindAttributesExist(compilation, Parameters, diagnostics, modifyCompilation: true); - ParameterHelpers.EnsureParamCollectionAttributeExistsAndModifyCompilation(compilation, Parameters, diagnostics); + ParameterHelpers.EnsureParamCollectionAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(Type)) { diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs index 3b3593634ed..952ebb3c4a1 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs @@ -522,10 +522,10 @@ internal sealed override ParameterSymbol? ExtensionParameter } } - internal sealed override string ExtensionGroupingName + internal sealed override string? ExtensionGroupingName => _underlyingType.ExtensionGroupingName; - internal sealed override string ExtensionMarkerName + internal sealed override string? ExtensionMarkerName => _underlyingType.ExtensionMarkerName; } } diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 2f8c60cb059..ffcb048a5ec 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -1758,7 +1758,7 @@ internal static bool IsCaptured(Symbol variable, SourceMethodSymbol containingSy } if (containingSymbol.GetIsNewExtensionMember() - && variable is ParameterSymbol { ContainingSymbol: TypeSymbol { IsExtension: true } }) + && variable is ParameterSymbol { ContainingSymbol: NamedTypeSymbol { IsExtension: true } }) { // An extension member doesn't capture the extension parameter return false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs index f1bec517298..ab506f56b4f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/SymbolExtensions.cs @@ -822,7 +822,7 @@ internal static int GetOverloadResolutionPriority(this Symbol symbol) internal static bool IsExtensionParameter(this ParameterSymbol parameter) { - return parameter.ContainingSymbol is TypeSymbol { IsExtension: true }; + return parameter.ContainingSymbol is NamedTypeSymbol { IsExtension: true }; } internal static bool IsExtensionParameterImplementation(this ParameterSymbol parameter) diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index a1ca8bd1fd9..b0f844cfa2d 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -230,6 +230,10 @@ protected void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArguments builder, PropertySymbol property) public override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string? ExtensionMarkerName => null; public override bool IsReadOnly => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs index 72d13cea137..04783e3cd73 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs @@ -817,11 +817,9 @@ public static bool CanCreateSingleElement(CSharpCompilation compilation) public override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionMarkerName => null; public override bool IsReadOnly => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs index f635e571f4b..4f5b4603fc0 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs @@ -147,6 +147,14 @@ internal bool InfersNullableAnnotation } } + /// + /// Gets the inferred nullable annotation of the backing field, + /// potentially binding and nullable-analyzing the associated get accessor. + /// + /// + /// The for this symbol does not expose this inferred nullable annotation. + /// For that API, the nullable annotation of the associated property is used instead. + /// internal NullableAnnotation GetInferredNullableAnnotation() { if (_inferredNullableAnnotation == (int)NullableAnnotation.Ignored) @@ -217,7 +225,7 @@ private NullableAnnotation ComputeInferredNullableAnnotation() DiagnosticBag nullableAnalyzeAndFilterDiagnostics(NullableAnnotation assumedNullableAnnotation) { var diagnostics = DiagnosticBag.GetInstance(); - NullableWalker.AnalyzeIfNeeded(binder, boundGetAccessor, boundGetAccessor.Syntax, diagnostics, getterNullResilienceData: (getAccessor, _property.BackingField, assumedNullableAnnotation)); + NullableWalker.AnalyzeIfNeeded(binder, boundGetAccessor, boundGetAccessor.Syntax, diagnostics, symbolAndGetterNullResilienceData: (getAccessor, new NullableWalker.GetterNullResilienceData(_property.BackingField, assumedNullableAnnotation))); if (diagnostics.IsEmptyWithoutResolution) { return diagnostics; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index af496c4dc61..d7985b29594 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -47,7 +47,11 @@ protected SynthesizedContainer(string name) internal sealed override bool IsInterface => this.TypeKind == TypeKind.Interface; - internal sealed override ParameterSymbol ExtensionParameter => null; +#nullable enable + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override string? ExtensionGroupingName => null; + internal sealed override string? ExtensionMarkerName => null; +#nullable disable internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) { @@ -149,12 +153,6 @@ internal override IEnumerable GetFieldsToEmit() public sealed override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); - - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); - public sealed override bool IsReadOnly => false; internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList basesBeingResolved) => ImmutableArray.Empty; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs index c4fa37192ac..c16455c17e7 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs @@ -90,12 +90,6 @@ public SynthesizedEmbeddedAttributeSymbolBase( public override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); - - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); - public override bool IsReadOnly => false; public override bool IsAbstract => false; @@ -121,7 +115,11 @@ internal override bool GetGuidString(out string guidString) internal override bool IsInterpolatedStringHandlerType => false; - internal sealed override ParameterSymbol ExtensionParameter => null; +#nullable enable + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override string? ExtensionGroupingName => null; + internal sealed override string? ExtensionMarkerName => null; +#nullable disable internal override bool HasSpecialName => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs index b97b37b2a46..71783a598a7 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs @@ -97,11 +97,8 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r public override bool IsStatic => false; public override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); - - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionGroupingName => null; + internal override string? ExtensionMarkerName => null; public override bool IsReadOnly => false; public override bool IsAbstract => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs index b8dd6a94129..306073ba333 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs @@ -65,11 +65,9 @@ internal SynthesizedInlineArrayTypeSymbol(SourceModuleSymbol containingModule, s public override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionMarkerName => null; public override bool IsReadOnly => true; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index 171a739d176..340bc952de2 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -194,11 +194,11 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_CodeAnalysis_UnscopedRefAttribute__ctor)); } - if (this.IsParamsArray && this.ContainingSymbol is SynthesizedDelegateInvokeMethod) + if (this.IsParamsArray) { AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_ParamArrayAttribute__ctor)); } - else if (this.IsParamsCollection && this.ContainingSymbol is SynthesizedDelegateInvokeMethod) + else if (this.IsParamsCollection) { AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeParamCollectionAttribute(this)); } @@ -363,6 +363,8 @@ public SynthesizedComplexParameterSymbol( Debug.Assert(isParams || !refCustomModifiers.IsEmpty || baseParameterForAttributes is object || defaultValue is not null || hasUnscopedRefAttribute); Debug.Assert(baseParameterForAttributes is null || baseParameterForAttributes.ExplicitDefaultConstantValue == defaultValue); Debug.Assert(baseParameterForAttributes is null || baseParameterForAttributes.RefKind == refKind); + Debug.Assert(!isParams || container is SynthesizedDelegateInvokeMethod or SynthesizedClosureMethod, + "If this fails, make sure we don't synthesize ParamsArrayAttribute for symbols we don't intend to."); _refCustomModifiers = refCustomModifiers; _baseParameterForAttributes = baseParameterForAttributes; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs index 40e271b8999..27af3c67995 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs @@ -58,11 +58,9 @@ public SynthesizedPrivateImplementationDetailsType(PrivateImplementationDetails public override bool IsRefLikeType => false; - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionGroupingName => null; - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal override string? ExtensionMarkerName => null; public override bool IsReadOnly => false; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index 971dea49f7b..7c2bba9f9b8 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -125,7 +125,12 @@ public override string Name internal override System.Reflection.MethodImplAttributes ImplementationAttributes { - get { return default(System.Reflection.MethodImplAttributes); } + get + { + var attributes = default(System.Reflection.MethodImplAttributes); + AddAsyncImplAttributeIfNeeded(ref attributes); + return attributes; + } } public override ImmutableArray TypeParameters diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index ae94e980832..30fe4bc2826 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -592,8 +592,6 @@ public sealed override bool IsValueType } } - internal sealed override ParameterSymbol ExtensionParameter => null; - internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) { return HasUnmanagedTypeConstraint ? ManagedKind.Unmanaged : ManagedKind.Managed; diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index aa96d556f8b..37ae1a788f3 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -555,18 +555,6 @@ public virtual bool IsAnonymousType /// internal virtual bool IsNativeIntegerWrapperType => false; -#nullable enable - public virtual bool IsExtension - => TypeKind == TypeKind.Extension; - - /// - /// For the type representing an extension declaration, returns the receiver parameter symbol. - /// It may be unnamed. - /// Note: this may be null even if is true, in error cases. - /// - internal abstract ParameterSymbol? ExtensionParameter { get; } -#nullable disable - internal bool IsNativeIntegerType => IsNativeIntegerWrapperType || (SpecialType is SpecialType.System_IntPtr or SpecialType.System_UIntPtr && this.ContainingAssembly.RuntimeSupportsNumericIntPtr); diff --git a/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs b/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs index e8001e59e11..9ce1d27ab03 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs +++ b/src/roslyn/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs @@ -903,7 +903,7 @@ private static bool NeedsSeparator(SyntaxToken token, SyntaxToken next) return true; } - if (IsKeyword(token.Kind())) + if (IsKeyword(token.Kind()) && !token.IsKind(SyntaxKind.ExtensionKeyword)) { if (!next.IsKind(SyntaxKind.ColonToken) && !next.IsKind(SyntaxKind.DotToken) && diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 643164d643e..9c850f22643 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -127,6 +127,11 @@ {0}: argument typu atributu nemůže používat parametry obecného typu. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. Atribut {0} není platný pro přístupové objekty události. Je platný jenom pro deklarace {1}. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např Operátor await jde použít jenom ve výrazu dotazu v rámci první kolekce výrazu počáteční klauzule from nebo v rámci výrazu kolekce klauzule join. - - 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. - V této asynchronní metodě chybí operátory await a spustí se synchronně. Zvažte použití operátoru await pro čekání na neblokující volání rozhraní API nebo vykonání činnosti vázané na procesor ve vlákně na pozadí pomocí výrazu await Task.Run(...). - - - - Async method lacks 'await' operators and will run synchronously - V této asynchronní metodě chybí operátory await a spustí se synchronně. - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Protože se toto volání neočekává, vykonávání aktuální metody pokračuje před dokončením volání. Zvažte použití operátoru await na výsledek volání. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index afa86f346e8..71d7f9101a2 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -127,6 +127,11 @@ „{0}“: Ein Attributtypargument kann keine Typparameter verwenden + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. Das Attribut "{0}" ist für Ereignisaccessoren nicht gültig. Es gilt nur für {1}-Deklarationen. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz Der await-Operator kann in einem Abfrageausdruck nur innerhalb des ersten Sammlungsausdrucks der ursprünglichen from-Klausel oder innerhalb des Sammlungsausdrucks einer join-Klausel verwendet werden. - - 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. - In dieser Async-Methode fehlen die "await"-Operatoren, weshalb sie synchron ausgeführt wird. Sie sollten die Verwendung des "await"-Operators oder von "await Task.Run(...)" in Betracht ziehen, um auf nicht blockierende API-Aufrufe zu warten bzw. CPU-gebundene Aufgaben auf einem Hintergrundthread auszuführen. - - - - Async method lacks 'await' operators and will run synchronously - Bei der asynchronen Methode fehlen "await"-Operatoren. Die Methode wird synchron ausgeführt. - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Da auf diesen Aufruf nicht gewartet wird, wird die Ausführung der aktuellen Methode vor Abschluss des Aufrufs fortgesetzt. Ziehen Sie ein Anwenden des "Await"-Operators auf das Ergebnis des Aufrufs in Betracht. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 65c23b88b85..5db9dd0dc9f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -127,6 +127,11 @@ "{0}": un argumento de tipo de atributo no puede usar parámetros de tipo + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. El atributo "{0}" no es válido en descriptores de acceso de eventos. Solo es válido en declaraciones "{1}". @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue El operador 'await' solo se puede usar en una expresión de consulta dentro de la primera expresión de colección de la cláusula 'from' inicial o de la expresión de colección de una cláusula 'join' - - 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. - El método asincrónico carece de operadores "await" y se ejecutará de forma sincrónica. Puede usar el operador 'await' para esperar llamadas API que no sean de bloqueo o 'await Task.Run(...)' para hacer tareas enlazadas a la CPU en un subproceso en segundo plano. - - - - Async method lacks 'await' operators and will run synchronously - El método asincrónico carece de operadores "await" y se ejecutará de forma sincrónica - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Como esta llamada no es 'awaited', la ejecución del método actual continuará antes de que se complete la llamada. Puede aplicar el operador 'await' al resultado de la llamada. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 212a6c506c1..ae96cc2518f 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -127,6 +127,11 @@ « {0} » : un argument d'attribut ne peut pas utiliser de paramètres de type + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. L'attribut '{0}' est non valide sur les accesseurs d'événement. Il est valide uniquement sur les déclarations '{1}'. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve L'opérateur 'await' peut seulement être utilisé dans une expression de requête dans la première expression de collection de la clause 'from' initiale ou dans l'expression de collection d'une clause 'join' - - 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. - Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone. Utilisez l'opérateur 'await' pour attendre les appels d'API non bloquants ou 'await Task.Run(…)' pour effectuer un travail utilisant le processeur sur un thread d'arrière-plan. - - - - Async method lacks 'await' operators and will run synchronously - Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Dans la mesure où cet appel n'est pas attendu, l'exécution de la méthode actuelle continue avant la fin de l'appel. Envisagez d'appliquer l'opérateur 'await' au résultat de l'appel. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 3e870819223..6e78268ea25 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -127,6 +127,11 @@ '{0}': un argomento di tipo di attributo non può usare parametri di tipo + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. L'attributo '{0}' non è valido nelle funzioni di accesso a eventi. È valido solo nelle dichiarazioni di '{1}'. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn È possibile usare l'operatore 'await' solo in espressioni di query all'interno della prima espressione di raccolta della clausola 'from' iniziale o all'interno dell'espressione di raccolta di una clausola 'join' - - 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. - In questo metodo asincrono non sono presenti operatori 'await', pertanto verrà eseguito in modo sincrono. Provare a usare l'operatore 'await' per attendere chiamate ad API non di blocco oppure 'await Task.Run(...)' per effettuare elaborazioni basate sulla CPU in un thread in background. - - - - Async method lacks 'await' operators and will run synchronously - Il metodo asincrono non contiene operatori 'await', pertanto verrà eseguito in modo sincrono - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Non è possibile attendere la chiamata, pertanto l'esecuzione del metodo corrente continuerà prima del completamento della chiamata. Provare ad applicare l'operatore 'await' al risultato della chiamata. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index ca4ab337a8a..a347b60bb32 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -127,6 +127,11 @@ '{0}': 属性の型引数では型パラメーターを使用することができません + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. 属性 '{0}' はイベント アクセサーでは無効です。'{1}' 宣言でのみ有効です。 @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ C# では out と ref を区別しますが、CLR では同じと認識します await' 演算子は、最初の 'from' 句の最初のコレクション式、または 'join' 句のコレクション式に含まれるクエリ式でのみ使用できます - - 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. - この非同期メソッドには 'await' 演算子がないため、同期的に実行されます。'await' 演算子を使用して非ブロッキング API 呼び出しを待機するか、'await Task.Run(...)' を使用してバックグラウンドのスレッドに対して CPU 主体の処理を実行することを検討してください。 - - - - Async method lacks 'await' operators and will run synchronously - 非同期メソッドは、'await' 演算子がないため、同期的に実行されます - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. この呼び出しを待たないため、現在のメソッドの実行は、呼び出しが完了するまで続行します。呼び出しの結果に 'await' 演算子を適用することを検討してください。 diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 02d31105871..4c63f7186ef 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -127,6 +127,11 @@ '{0}': 특성 유형 인수는 형식 매개 변수를 사용할 수 없습니다. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. 이벤트 접근자에서 '{0}' 특성이 유효하지 않습니다. 이 특성은 '{1}' 선언에만 유효합니다. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 await' 연산자는 초기 'from' 절의 첫 번째 Collection 식이나 'join' 절의 Collection 식 내의 쿼리 식에서만 사용할 수 있습니다. - - 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. - 이 비동기 메서드에는 'await' 연산자가 없으며 메서드가 동시에 실행됩니다. 'await' 연산자를 사용하여 비블로킹 API 호출을 대기하거나, 'await Task.Run(...)'을 사용하여 백그라운드 스레드에서 CPU 바인딩된 작업을 수행하세요. - - - - Async method lacks 'await' operators and will run synchronously - 이 비동기 메서드에는 'await' 연산자가 없으며 메서드가 동시에 실행됩니다. - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. 이 호출이 대기되지 않으므로 호출이 완료되기 전에 현재 메서드가 계속 실행됩니다. 호출 결과에 'await' 연산자를 적용해 보세요. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index a2bbf3c3a27..acbc2143560 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -127,6 +127,11 @@ "{0}": argument typu atrybut nie może używać parametrów typu + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. Atrybut „{0}” nie jest prawidłowy w metodach dostępu zdarzenia. Jest on prawidłowy tylko w deklaracjach „{1}”. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada Operatora „await” można użyć tylko w wyrażeniu zapytania w pierwszym wyrażeniu kolekcji początkowej klauzuli „from” albo w wyrażeniu kolekcji klauzuli „join”. - - 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. - W tej metodzie asynchronicznej brakuje operatorów „await”, dlatego będzie wykonywana synchronicznie. Rozważ możliwość użycia operatora „await” w celu zdefiniowania oczekiwania na nieblokujące wywołania interfejsów API albo wyrażenia „await Task.Run(...)” w celu przeniesienia wykonywania zadań intensywnie angażujących procesor do wątku w tle. - - - - Async method lacks 'await' operators and will run synchronously - Metoda asynchroniczna nie zawiera operatorów „await” i zostanie uruchomiona synchronicznie - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Ponieważ to wywołanie nie jest oczekiwane, wykonywanie bieżącej metody będzie kontynuowane bez oczekiwania na ukończenie wywołania. Rozważ możliwość zastosowania operatora „await” do wyniku wywołania. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index ee3a6d1bd9a..a76920c1042 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -127,6 +127,11 @@ '{0}': um argumento de tipo de atributo não pode usar parâmetro de tipo + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. O atributo '{0}' não é válido em acessadores de evento. Ele é válido somente em declarações '{1}'. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc O operador 'await' só pode ser usado em uma expressão de consulta na primeira expressão de coleção da cláusula 'from' inicial ou na expressão de coleção de uma cláusula 'join' - - 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. - Este método assíncrono não possui operadores 'await' e será executado de modo síncrono. É recomendável o uso do operador 'await' para aguardar chamadas à API desbloqueadas ou do operador 'await Task.Run(...)' para realizar um trabalho associado à CPU em um thread em segundo plano. - - - - Async method lacks 'await' operators and will run synchronously - O método assíncrono não possui operadores 'await' e será executado de forma síncrona - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Como esta chamada não é aguardada, a execução do método atual continua antes da conclusão da chamada. Considere aplicar o operador 'await' ao resultado da chamada. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index b495e9dbada..b823fcf193c 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -127,6 +127,11 @@ "{0}": аргумент типа атрибута не может использовать параметры типа + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. Атрибут "{0}" запрещено использовать в методах доступа к событиям. Он допустим только для объявлений "{1}". @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11121,17 +11126,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi Оператор await можно использовать только в выражении запроса в первом выражении коллекции начального предложения From или в выражении коллекции предложения Join. - - 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. - В данном асинхронном методе отсутствуют операторы await, поэтому метод будет выполняться синхронно. Воспользуйтесь оператором await для ожидания неблокирующих вызовов API или оператором await Task.Run(...) для выполнения связанных с ЦП заданий в фоновом потоке. - - - - Async method lacks 'await' operators and will run synchronously - В асинхронном методе отсутствуют операторы await, будет выполнен синхронный метод - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова. Попробуйте применить оператор await к результату вызова. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5d9afa657ae..b31cd0fa52b 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -127,6 +127,11 @@ '{0}': öznitelik tür bağımsız değişkeni tür parametreleri kullanamaz + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. '{0}' özniteliği olay erişimcilerinde geçerli değil. Bu öznitelik yalnızca '{1}' bildirimlerinde geçerlidir. @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl await' işleci yalnızca başlangıçtaki 'from' yan tümcesinin ilk koleksiyon ifadesinin içindeki ya da bir 'join' yan tümcesinin toplama ifadesinin içindeki bir sorgu ifadesinde kullanılabilir - - 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. - Bu zaman uyumsuz yöntemde 'await' işleçleri yok ve zaman uyumlu çalışacak. 'await' işlecini kullanarak engelleyici olmayan API çağrılarını beklemeyi veya 'await Task.Run(...)' kullanarak bir arka plan iş parçacığında CPU bağlantılı iş yapmayı düşünün. - - - - Async method lacks 'await' operators and will run synchronously - Zaman uyumsuz yöntemde 'await' işleçleri yok ve zaman uyumlu çalışacak - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. Bu çağrı beklenmediğinden, çağrı tamamlanmadan geçerli yöntemin yürütülmesi devam eder. Çağrının sonucuna 'await' işlecini eklemeyi düşünün. diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 1e48fd78b3e..5a9c1affb06 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -127,6 +127,11 @@ “{0}”: 特性类型参数不能使用类型参数 + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. 特性“{0}”对事件访问器无效。它仅对“{1}”声明有效。 @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi "await" 运算符只能用在初始 "from" 子句的第一个集合表达式或 "join" 子句的集合表达式内的查询表达式中 - - 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. - 此异步方法缺少 "await" 运算符,将以同步方式运行。请考虑使用 "await" 运算符等待非阻止的 API 调用,或者使用 "await Task.Run(...)" 在后台线程上执行占用大量 CPU 的工作。 - - - - Async method lacks 'await' operators and will run synchronously - 异步方法缺少 "await" 运算符,将以同步方式运行 - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. 由于此调用不会等待,因此在此调用完成之前将会继续执行当前方法。请考虑将 "await" 运算符应用于调用结果。 diff --git a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index cc7924cacd9..5b004ca8054 100644 --- a/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -127,6 +127,11 @@ '{0}': 屬性型別引數不可使用型別參數 + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + Attribute '{0}' is not valid on event accessors. It is only valid on '{1}' declarations. 屬性 '{0}' 在事件存取子上無效。其只有在 '{1}' 宣告上才有效。 @@ -1525,7 +1530,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. - + 'MethodImplAttribute.Async' and 'async' are not localizable. An extension member syntax is disallowed in nested position within an extension member syntax @@ -11120,17 +11125,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi await' 運算子只能用在初始 'from' 子句的第一個集合運算式或 'join' 子句的集合運算式中的查詢運算式 - - 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. - 這個非同步方法缺少 'await' 運算子,因此將以同步方式執行。請考慮使用 'await' 運算子等候未封鎖的應用程式開發介面呼叫,或使用 'await Task.Run(...)' 在背景執行緒上執行 CPU-bound 工作。 - - - - Async method lacks 'await' operators and will run synchronously - Async 方法缺乏 'await' 運算子,將同步執行 - - - + Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. 因為未等候此呼叫,所以在呼叫完成之前會繼續執行目前的方法。請考慮將 'await' 運算子套用至呼叫的結果。 diff --git a/src/roslyn/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/roslyn/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 0b5da551687..92201963a4e 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -8717,9 +8717,7 @@ public void CS1691WRN_BadWarningNumber_Bug15905() string source = Temp.CreateFile(prefix: "", extension: ".cs").WriteAllText(@" class Program { -#pragma warning disable 1998 public static void Main() { } -#pragma warning restore 1998 } ").Path; var outWriter = new StringWriter(CultureInfo.InvariantCulture); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs index bddf43f5220..979dd03270b 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncEHTests.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncEHTests : EmitMetadataTestBase { private static readonly MetadataReference[] s_asyncRefs = new[] { MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929 }; @@ -687,11 +688,7 @@ public static void Main() [G]: Unexpected type on the stack. { Offset = 0x13, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); } [Fact] @@ -738,11 +735,7 @@ public static void Main() [H]: Unexpected type on the stack. { Offset = 0xa, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); } [Fact] @@ -903,11 +896,7 @@ .locals init (int V_0, [G]: Unexpected type on the stack. { Offset = 0x29, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { @@ -1007,11 +996,7 @@ public static void Main() [F]: Unexpected type on the stack. { Offset = 0xb, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { @@ -1367,11 +1352,7 @@ .locals init (int V_0, [G]: Unexpected type on the stack. { Offset = 0x48, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { @@ -1488,11 +1469,7 @@ public static void Main() [G]: Unexpected type on the stack. { Offset = 0x3e, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { @@ -1772,11 +1749,7 @@ .locals init (int V_0, [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (6,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { // Code size 72 (0x48) @@ -2050,11 +2023,7 @@ .locals init (int V_0, [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (6,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G(System.Threading.SemaphoreSlim)", """ { // Code size 43 (0x2b) @@ -2303,11 +2272,7 @@ .locals init (int V_0, [F]: Unexpected type on the stack. { Offset = 0x1, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (6,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(6, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G(System.Threading.SemaphoreSlim)", """ { // Code size 43 (0x2b) @@ -2385,11 +2350,7 @@ public static void Main() [G]: Unexpected type on the stack. { Offset = 0x45, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (5,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(5, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { // Code size 70 (0x46) @@ -2858,11 +2819,7 @@ .locals init (int V_0, [G]: Unexpected type on the stack. { Offset = 0x1f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } """ }); - verifier.VerifyDiagnostics( - // (7,28): 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 Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(7, 28) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.G()", """ { @@ -4012,7 +3969,6 @@ class Exception2 : Exception { } public void NestedRethrow_02(bool await1, bool await2, bool await3) { var source = $$""" - #pragma warning disable 1998 // async method lacks 'await' operators using System; using System.Threading.Tasks; diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 6b49e879285..d745bf90704 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -31,7 +31,7 @@ internal enum Instruction YieldBreak } - [CompilerTrait(CompilerFeature.AsyncStreams)] + [CompilerTrait(CompilerFeature.AsyncStreams, CompilerFeature.Async)] public class CodeGenAsyncIteratorTests : EmitMetadataTestBase { internal static string ExpectedOutput(string output) @@ -1713,9 +1713,6 @@ async System.Collections.Generic.IAsyncEnumerable M2() // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return 1; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(8, 9), - // (10,60): 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 System.Collections.Generic.IAsyncEnumerable M2() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M2").WithLocation(10, 60), // (12,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return 4; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(12, 9) @@ -1744,9 +1741,6 @@ async System.Collections.Generic.IAsyncEnumerable M2() // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return null; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(8, 9), - // (10,60): 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 System.Collections.Generic.IAsyncEnumerable M2() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M2").WithLocation(10, 60), // (12,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return null; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(12, 9) @@ -1781,9 +1775,6 @@ async System.Collections.Generic.IAsyncEnumerable M2(ref string s2) // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return ref s; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(8, 9), - // (10,60): 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 System.Collections.Generic.IAsyncEnumerable M2(ref string s2) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M2").WithLocation(10, 60), // (10,74): error CS1988: Async methods cannot have ref, in or out parameters // async System.Collections.Generic.IAsyncEnumerable M2(ref string s2) Diagnostic(ErrorCode.ERR_BadAsyncArgType, "s2").WithLocation(10, 74), @@ -1815,9 +1806,6 @@ async System.Collections.Generic.IAsyncEnumerable M2() // (8,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return default; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(8, 9), - // (10,60): 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 System.Collections.Generic.IAsyncEnumerable M2() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M2").WithLocation(10, 60), // (12,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return default; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(12, 9) @@ -1943,9 +1931,6 @@ async System.Collections.Generic.IAsyncEnumerator M() }"; var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( - // (4,60): 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 System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60), // (6,9): error CS1622: Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration. // return null; Diagnostic(ErrorCode.ERR_ReturnInIterator, "return").WithLocation(6, 9) @@ -2054,15 +2039,8 @@ public static async System.Collections.Generic.IAsyncEnumerable M() } }"; var comp = CreateCompilationWithAsyncIterator(source); - comp.VerifyDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74) - ); + comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), // (4,74): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing 'async' from the method declaration or adding a 'yield' statement. // public static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithLocation(4, 74) @@ -2086,15 +2064,8 @@ public static async System.Collections.Generic.IAsyncEnumerator M() } }"; var comp = CreateCompilationWithAsyncIterator(source); - comp.VerifyDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74) - ); + comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), // (4,74): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing `async` from the method declaration. // public static async System.Collections.Generic.IAsyncEnumerator M() Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithLocation(4, 74) @@ -2137,15 +2108,8 @@ async System.Collections.Generic.IAsyncEnumerator M() } }"; var comp = CreateCompilationWithAsyncIterator(source); - comp.VerifyDiagnostics( - // (4,60): 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 System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60) - ); + comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( - // (4,60): 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 System.Collections.Generic.IAsyncEnumerator M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60), // (4,60): error CS8420: The body of an async-iterator method must contain a 'yield' statement. Consider removing `async` from the method declaration. // async System.Collections.Generic.IAsyncEnumerator M() Diagnostic(ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait, "M").WithLocation(4, 60) @@ -2165,9 +2129,6 @@ public static async System.Collections.Generic.IAsyncEnumerable M() }"; var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), // (4,74): error CS0161: 'C.M()': not all code paths return a value // public static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 74) @@ -2187,11 +2148,7 @@ public static async System.Collections.Generic.IAsyncEnumerable M() } }"; var comp = CreateCompilationWithAsyncIterator(new[] { Run(iterations: 2), source }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74) - ); + comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1 END DISPOSAL DONE"); } @@ -2258,10 +2215,7 @@ static async System.Collections.Generic.IEnumerable M() comp.VerifyDiagnostics( // (4,62): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // static async System.Collections.Generic.IEnumerable M() - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 62), - // (4,62): 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 System.Collections.Generic.IEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 62) + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "M").WithLocation(4, 62) ); } @@ -2278,9 +2232,6 @@ public static async System.Collections.Generic.IAsyncEnumerator M() }"; var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( - // (4,74): 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. - // public static async System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 74), // (4,74): error CS0161: 'C.M()': not all code paths return a value // public static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 74) @@ -2300,11 +2251,7 @@ static async System.Collections.Generic.IAsyncEnumerator M(int value) } }"; var comp = CreateCompilationWithAsyncIterator(source); - comp.VerifyDiagnostics( - // (4,67): 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 System.Collections.Generic.IAsyncEnumerator M(int value) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 67) - ); + comp.VerifyDiagnostics(); } [Fact] @@ -6618,11 +6565,7 @@ public static async System.Threading.Tasks.Task Main() } }"; var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (4,67): 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 System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 67) - ); + comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "1"); } @@ -6646,11 +6589,7 @@ public static async System.Threading.Tasks.Task Main() } }"; var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (4,67): 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 System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 67) - ); + comp.VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "none"); } @@ -6666,9 +6605,6 @@ async System.Collections.Generic.IAsyncEnumerable M() }"; var comp = CreateCompilationWithAsyncIterator(source); comp.VerifyDiagnostics( - // (4,60): 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 System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60), // (4,60): error CS0161: 'C.M()': not all code paths return a value // async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 60) @@ -6694,10 +6630,7 @@ async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_EmptyYield, "return").WithLocation(7, 15), // (6,22): error CS0029: Cannot implicitly convert type 'string' to 'int' // yield return "hello"; - Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""hello""").WithArguments("string", "int").WithLocation(6, 22), - // (4,60): 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 System.Collections.Generic.IAsyncEnumerable M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 60) + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""hello""").WithArguments("string", "int").WithLocation(6, 22) ); } @@ -8895,7 +8828,6 @@ static async System.Threading.Tasks.Task Main() public void LambdaWithBindingErrorInYieldReturn() { var src = """ -#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -8916,7 +8848,6 @@ static async IAsyncEnumerable>> BarAsync() comp.VerifyDiagnostics(); src = """ -#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -8937,21 +8868,22 @@ static async IAsyncEnumerable>> BarAsync() comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( - // (12,13): error CS0118: 's' is a variable but is used like a type + // (11,13): error CS0118: 's' is a variable but is used like a type // s // 1 - Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(12, 13), - // (13,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(11, 13), + // (12,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(13, 13), - // (13,13): warning CS0168: The variable 'await' is declared but never used + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(12, 13), + // (12,13): warning CS0168: The variable 'await' is declared but never used // await Task.CompletedTask; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(13, 13), - // (13,19): error CS1002: ; expected + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(12, 13), + // (12,19): error CS1002: ; expected // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(13, 19), - // (13,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(12, 19), + // (12,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(13, 19)); + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19) + ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -8964,7 +8896,6 @@ static async IAsyncEnumerable>> BarAsync() public void LambdaWithBindingErrorInReturn() { var src = """ -#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously using System; using System.Threading.Tasks; @@ -8984,7 +8915,6 @@ static async Task>> BarAsync() comp.VerifyDiagnostics(); src = """ -#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously using System; using System.Threading.Tasks; @@ -9003,21 +8933,22 @@ static async Task>> BarAsync() """; comp = CreateCompilation(src, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( - // (11,13): error CS0118: 's' is a variable but is used like a type + // (10,13): error CS0118: 's' is a variable but is used like a type // s // 1 - Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(11, 13), - // (12,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(10, 13), + // (11,13): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(12, 13), - // (12,13): warning CS0168: The variable 'await' is declared but never used + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(11, 13), + // (11,13): warning CS0168: The variable 'await' is declared but never used // await Task.CompletedTask; - Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(12, 13), - // (12,19): error CS1002: ; expected + Diagnostic(ErrorCode.WRN_UnreferencedVar, "await").WithArguments("await").WithLocation(11, 13), + // (11,19): error CS1002: ; expected // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(12, 19), - // (12,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + Diagnostic(ErrorCode.ERR_SemicolonExpected, "Task").WithLocation(11, 19), + // (11,19): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // await Task.CompletedTask; - Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(12, 19)); + Diagnostic(ErrorCode.ERR_IllegalStatement, "Task.CompletedTask").WithLocation(11, 19) + ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); @@ -10870,9 +10801,7 @@ public void Repro_78640() static class C { - #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public static async IAsyncEnumerable AsAsyncEnumerable(this IEnumerable enumerable, [EnumeratorCancellation] CancellationToken cancellationToken) - #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { ArgumentNullException.ThrowIfNull(enumerable); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs index 3bbd16b6f85..95e4fa9f304 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncLocalsTests.cs @@ -16,6 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncLocalsTests : EmitMetadataTestBase { private static readonly MetadataReference[] s_asyncRefs = new[] { MscorlibRef_v4_0_30316_17626, SystemRef_v4_0_30319_17929, SystemCoreRef_v4_0_30319_17929 }; diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs index a822b966564..a13a3dca714 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMainTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - [CompilerTrait(CompilerFeature.AsyncMain)] + [CompilerTrait(CompilerFeature.AsyncMain, CompilerFeature.Async)] public class CodeGenAsyncMainTests : EmitMetadataTestBase { [Fact] @@ -1271,12 +1271,6 @@ async static Task Main(string[] args) } }"; var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseDebugExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)).VerifyDiagnostics( - // (6,28): 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 static Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(6, 28), - // (12,30): 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 static Task Main(string[] args) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(12, 30), // (6,18): error CS8107: Feature 'async main' is not available in C# 7. Please use language version 7.1 or greater. // async static Task Main() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Task").WithArguments("async main", "7.1").WithLocation(6, 18), diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs index fc204d27f2e..2d74fd74e00 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncMethodBuilderOverrideTests : EmitMetadataTestBase { private const string AsyncMethodBuilderAttribute = diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 8ae68599773..ca2758c661b 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncSpillTests : EmitMetadataTestBase { public CodeGenAsyncSpillTests() @@ -553,7 +554,6 @@ .maxstack 2 public void SpillNestedUnary() { var source = @" -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System; using System.Threading.Tasks; @@ -1119,6 +1119,8 @@ public static async Task F(int[] array) "; var v = CompileAndVerify(source, options: TestOptions.DebugDll); + // https://github.com/dotnet/roslyn/issues/80147 - There's an extra unneeded array + // load before the await that could be removed v.VerifyIL("Test.d__2.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"{ // Code size 273 (0x111) @@ -1255,64 +1257,66 @@ .locals init (int V_0, }", sequencePoints: "Test+d__2.MoveNext"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (18,38): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(array[1] += 2, array[3] += await G(), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 38) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 54 (0x36) - // .maxstack 4 - // .locals init (int& V_0, - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.1 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: ldc.i4.2 - // IL_000a: add - // IL_000b: dup - // IL_000c: stloc.3 - // IL_000d: stind.i4 - // IL_000e: ldloc.3 - // IL_000f: ldarg.0 - // IL_0010: ldc.i4.3 - // IL_0011: ldelema "int" - // IL_0016: stloc.0 - // IL_0017: ldloc.0 - // IL_0018: ldind.i4 - // IL_0019: stloc.1 - // IL_001a: call "System.Threading.Tasks.Task Test.G()" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.2 - // IL_0025: ldloc.0 - // IL_0026: ldloc.1 - // IL_0027: ldloc.2 - // IL_0028: add - // IL_0029: dup - // IL_002a: stloc.3 - // IL_002b: stind.i4 - // IL_002c: ldloc.3 - // IL_002d: ldc.i4.4 - // IL_002e: call "int Test.H(int, int, int)" - // IL_0033: pop - // IL_0034: ldc.i4.1 - // IL_0035: ret - // } - // """); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x40, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 65 (0x41) + .maxstack 4 + .locals init (int[] V_0, + int& V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: ldelema "int" + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldc.i4.2 + IL_000a: add + IL_000b: dup + IL_000c: stloc.s V_4 + IL_000e: stind.i4 + IL_000f: ldloc.s V_4 + IL_0011: ldarg.0 + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: ldc.i4.3 + IL_0015: ldelem.i4 + IL_0016: pop + IL_0017: ldloc.0 + IL_0018: ldc.i4.3 + IL_0019: ldelem.i4 + IL_001a: stloc.2 + IL_001b: call "System.Threading.Tasks.Task Test.G()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.3 + IL_0026: ldloc.0 + IL_0027: ldc.i4.3 + IL_0028: ldelema "int" + IL_002d: stloc.1 + IL_002e: ldloc.1 + IL_002f: ldloc.2 + IL_0030: ldloc.3 + IL_0031: add + IL_0032: dup + IL_0033: stloc.s V_4 + IL_0035: stind.i4 + IL_0036: ldloc.s V_4 + IL_0038: ldc.i4.4 + IL_0039: call "int Test.H(int, int, int)" + IL_003e: pop + IL_003f: ldc.i4.1 + IL_0040: ret + } + """); } [Fact] @@ -1468,164 +1472,180 @@ .locals init (int V_0, }", sequencePoints: "Test+d__2.MoveNext"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (18,38): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(array[1] += 2, array[3] += await G(), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 38) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x35, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 54 (0x36) - // .maxstack 4 - // .locals init (int& V_0, - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.1 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: ldc.i4.2 - // IL_000a: add - // IL_000b: dup - // IL_000c: stloc.3 - // IL_000d: stind.i4 - // IL_000e: ldloc.3 - // IL_000f: ldarg.0 - // IL_0010: ldc.i4.3 - // IL_0011: ldelema "int" - // IL_0016: stloc.0 - // IL_0017: ldloc.0 - // IL_0018: ldind.i4 - // IL_0019: stloc.1 - // IL_001a: call "System.Threading.Tasks.Task Test.G()" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.2 - // IL_0025: ldloc.0 - // IL_0026: ldloc.1 - // IL_0027: ldloc.2 - // IL_0028: add - // IL_0029: dup - // IL_002a: stloc.3 - // IL_002b: stind.i4 - // IL_002c: ldloc.3 - // IL_002d: ldc.i4.4 - // IL_002e: call "int Test.H(int, int, int)" - // IL_0033: pop - // IL_0034: ldc.i4.1 - // IL_0035: ret - // } - // """); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x40, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 65 (0x41) + .maxstack 4 + .locals init (int[] V_0, + int& V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 + IL_0002: ldelema "int" + IL_0007: dup + IL_0008: ldind.i4 + IL_0009: ldc.i4.2 + IL_000a: add + IL_000b: dup + IL_000c: stloc.s V_4 + IL_000e: stind.i4 + IL_000f: ldloc.s V_4 + IL_0011: ldarg.0 + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: ldc.i4.3 + IL_0015: ldelem.i4 + IL_0016: pop + IL_0017: ldloc.0 + IL_0018: ldc.i4.3 + IL_0019: ldelem.i4 + IL_001a: stloc.2 + IL_001b: call "System.Threading.Tasks.Task Test.G()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.3 + IL_0026: ldloc.0 + IL_0027: ldc.i4.3 + IL_0028: ldelema "int" + IL_002d: stloc.1 + IL_002e: ldloc.1 + IL_002f: ldloc.2 + IL_0030: ldloc.3 + IL_0031: add + IL_0032: dup + IL_0033: stloc.s V_4 + IL_0035: stind.i4 + IL_0036: ldloc.s V_4 + IL_0038: ldc.i4.4 + IL_0039: call "int Test.H(int, int, int)" + IL_003e: pop + IL_003f: ldc.i4.1 + IL_0040: ret + } + """); } [Fact] public void SpillSequencesInConditionalExpression1() { var source = @" +using System; using System.Threading.Tasks; public class Test { public static int H(int a, int b, int c) { + Console.Write($""H{a},{b},{c};""); return a; } - public static Task G() + public static Task G(int i) { - return null; + Console.Write($""G{i}""); + return Task.FromResult(i); } public static async Task F(int[] array) { - H(0, (1 == await G()) ? array[3] += await G() : 1, 4); + H(0, (1 == await G(1)) ? array[3] += await G(2) : 1, 4); return 1; } + + public static async Task Main() + { + await F(new int[4]); + } } "; - CompileAndVerify(source, options: TestOptions.DebugDll); + + var expectedOutput = "G1G2H0,2,4;"; + CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (18,45): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, (1 == await G()) ? array[3] += await G() : 1, 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(18, 45) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x3c, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 61 (0x3d) - // .maxstack 3 - // .locals init (int V_0, - // int V_1, - // int V_2, - // int V_3, - // int V_4) - // IL_0000: call "System.Threading.Tasks.Task Test.G()" - // IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000a: stloc.0 - // IL_000b: ldc.i4.1 - // IL_000c: ldloc.0 - // IL_000d: bne.un.s IL_0030 - // IL_000f: ldarg.0 - // IL_0010: ldc.i4.3 - // IL_0011: ldelema "int" - // IL_0016: dup - // IL_0017: ldind.i4 - // IL_0018: stloc.2 - // IL_0019: call "System.Threading.Tasks.Task Test.G()" - // IL_001e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0023: stloc.3 - // IL_0024: ldloc.2 - // IL_0025: ldloc.3 - // IL_0026: add - // IL_0027: dup - // IL_0028: stloc.s V_4 - // IL_002a: stind.i4 - // IL_002b: ldloc.s V_4 - // IL_002d: stloc.1 - // IL_002e: br.s IL_0032 - // IL_0030: ldc.i4.1 - // IL_0031: stloc.1 - // IL_0032: ldc.i4.0 - // IL_0033: ldloc.1 - // IL_0034: ldc.i4.4 - // IL_0035: call "int Test.H(int, int, int)" - // IL_003a: pop - // IL_003b: ldc.i4.1 - // IL_003c: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x11 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 68 (0x44) + .maxstack 3 + .locals init (int V_0, + int V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.1 + IL_0001: call "System.Threading.Tasks.Task Test.G(int)" + IL_0006: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: stloc.0 + IL_000c: ldc.i4.1 + IL_000d: ldloc.0 + IL_000e: bne.un.s IL_0037 + IL_0010: ldarg.0 + IL_0011: dup + IL_0012: ldc.i4.3 + IL_0013: ldelem.i4 + IL_0014: pop + IL_0015: dup + IL_0016: ldc.i4.3 + IL_0017: ldelem.i4 + IL_0018: stloc.2 + IL_0019: ldc.i4.2 + IL_001a: call "System.Threading.Tasks.Task Test.G(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.3 + IL_0025: ldc.i4.3 + IL_0026: ldelema "int" + IL_002b: ldloc.2 + IL_002c: ldloc.3 + IL_002d: add + IL_002e: dup + IL_002f: stloc.s V_4 + IL_0031: stind.i4 + IL_0032: ldloc.s V_4 + IL_0034: stloc.1 + IL_0035: br.s IL_0039 + IL_0037: ldc.i4.1 + IL_0038: stloc.1 + IL_0039: ldc.i4.0 + IL_003a: ldloc.1 + IL_003b: ldc.i4.4 + IL_003c: call "int Test.H(int, int, int)" + IL_0041: pop + IL_0042: ldc.i4.1 + IL_0043: ret + } + """); } [Fact] public void SpillSequencesInNullCoalescingOperator1() { var source = @" +using System; using System.Threading.Tasks; public class C { public static int H(int a, object b, int c) { + Console.Write($""H{a},{b},{c};""); return a; } @@ -1634,19 +1654,27 @@ public static object O(int a) return null; } - public static Task G() + public static Task G(int i) { - return null; + Console.Write($""G{i};""); + return Task.FromResult(i); } public static async Task F(int[] array) { - H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); + H(0, O(array[0] += await G(1)) ?? (array[1] += await G(2)), 4); return 1; } + + public static async Task Main() + { + await F(new int[4]); + } } "; - CompileAndVerify(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => + + var expectedOutput = "G1;G2;H0,2,4;"; + CompileAndVerify(source, options: TestOptions.ReleaseExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: expectedOutput, symbolValidator: module => { AssertEx.Equal(new[] { @@ -1661,7 +1689,7 @@ public static async Task F(int[] array) }, module.GetFieldNames("C.d__3")); }); - CompileAndVerify(source, verify: Verification.Passes, options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module => + CompileAndVerify(source, verify: Verification.Passes, options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), expectedOutput: expectedOutput, symbolValidator: module => { AssertEx.Equal(new[] { @@ -1681,80 +1709,84 @@ public static async Task F(int[] array) }); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,28): error CS9328: Method 'C.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("C.F(int[])").WithLocation(23, 28), - // (23,55): error CS9328: Method 'C.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, O(array[0] += await G()) ?? (array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("C.F(int[])").WithLocation(23, 55) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x55, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("C.F(int[])", """ - // { - // // Code size 86 (0x56) - // .maxstack 3 - // .locals init (int V_0, - // int V_1, - // object V_2, - // int V_3, - // int V_4, - // int V_5) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.0 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: stloc.0 - // IL_000a: call "System.Threading.Tasks.Task C.G()" - // IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0014: stloc.1 - // IL_0015: ldloc.0 - // IL_0016: ldloc.1 - // IL_0017: add - // IL_0018: dup - // IL_0019: stloc.3 - // IL_001a: stind.i4 - // IL_001b: ldloc.3 - // IL_001c: call "object C.O(int)" - // IL_0021: stloc.2 - // IL_0022: ldloc.2 - // IL_0023: brtrue.s IL_004b - // IL_0025: ldarg.0 - // IL_0026: ldc.i4.1 - // IL_0027: ldelema "int" - // IL_002c: dup - // IL_002d: ldind.i4 - // IL_002e: stloc.3 - // IL_002f: call "System.Threading.Tasks.Task C.G()" - // IL_0034: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0039: stloc.s V_4 - // IL_003b: ldloc.3 - // IL_003c: ldloc.s V_4 - // IL_003e: add - // IL_003f: dup - // IL_0040: stloc.s V_5 - // IL_0042: stind.i4 - // IL_0043: ldloc.s V_5 - // IL_0045: box "int" - // IL_004a: stloc.2 - // IL_004b: ldc.i4.0 - // IL_004c: ldloc.2 - // IL_004d: ldc.i4.4 - // IL_004e: call "int C.H(int, object, int)" - // IL_0053: pop - // IL_0054: ldc.i4.1 - // IL_0055: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x61, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Main]: Return value missing on the stack. { Offset = 0x11 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.F(int[])", """ + { + // Code size 98 (0x62) + .maxstack 3 + .locals init (int V_0, + int V_1, + object V_2, + int V_3, + int V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: ldc.i4.0 + IL_0003: ldelem.i4 + IL_0004: pop + IL_0005: dup + IL_0006: ldc.i4.0 + IL_0007: ldelem.i4 + IL_0008: stloc.0 + IL_0009: ldc.i4.1 + IL_000a: call "System.Threading.Tasks.Task C.G(int)" + IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0014: stloc.1 + IL_0015: ldc.i4.0 + IL_0016: ldelema "int" + IL_001b: ldloc.0 + IL_001c: ldloc.1 + IL_001d: add + IL_001e: dup + IL_001f: stloc.3 + IL_0020: stind.i4 + IL_0021: ldloc.3 + IL_0022: call "object C.O(int)" + IL_0027: stloc.2 + IL_0028: ldloc.2 + IL_0029: brtrue.s IL_0057 + IL_002b: ldarg.0 + IL_002c: dup + IL_002d: ldc.i4.1 + IL_002e: ldelem.i4 + IL_002f: pop + IL_0030: dup + IL_0031: ldc.i4.1 + IL_0032: ldelem.i4 + IL_0033: stloc.3 + IL_0034: ldc.i4.2 + IL_0035: call "System.Threading.Tasks.Task C.G(int)" + IL_003a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003f: stloc.s V_4 + IL_0041: ldc.i4.1 + IL_0042: ldelema "int" + IL_0047: ldloc.3 + IL_0048: ldloc.s V_4 + IL_004a: add + IL_004b: dup + IL_004c: stloc.s V_5 + IL_004e: stind.i4 + IL_004f: ldloc.s V_5 + IL_0051: box "int" + IL_0056: stloc.2 + IL_0057: ldc.i4.0 + IL_0058: ldloc.2 + IL_0059: ldc.i4.4 + IL_005a: call "int C.H(int, object, int)" + IL_005f: pop + IL_0060: ldc.i4.1 + IL_0061: ret + } + """); } [WorkItem(4628, "https://github.com/dotnet/roslyn/issues/4628")] @@ -1996,10 +2028,7 @@ Not Valid! verifier.VerifyDiagnostics( // (23,17): warning CS8073: The result of the expression is always 'true' since a value of type 'Guid' is never equal to 'null' of type 'Guid?' // if (item.Item2 != null || await IsValid(item.Item2)) - Diagnostic(ErrorCode.WRN_NubExprIsConstBool2, "item.Item2 != null").WithArguments("true", "System.Guid", "System.Guid?").WithLocation(23, 17), - // (29,41): 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. - // private static async Task IsValid(Guid id) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "IsValid").WithLocation(29, 41) + Diagnostic(ErrorCode.WRN_NubExprIsConstBool2, "item.Item2 != null").WithArguments("true", "System.Guid", "System.Guid?").WithLocation(23, 17) ); verifier.VerifyIL("AsyncConditionalBug.Program.DoSomething(System.Tuple)", """ { @@ -2061,80 +2090,81 @@ public static async Task F(int[] array) CompileAndVerify(source, options: TestOptions.DebugDll); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,28): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, B(array[0] += await G()) || B(array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(23, 28), - // (23,56): error CS9328: Method 'Test.F(int[])' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // H(0, B(array[0] += await G()) || B(array[1] += await G()), 4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await G()").WithArguments("Test.F(int[])").WithLocation(23, 56) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F]: Unexpected type on the stack. { Offset = 0x55, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Test.F(int[])", """ - // { - // // Code size 86 (0x56) - // .maxstack 3 - // .locals init (int V_0, - // int V_1, - // bool V_2, - // int V_3, - // int V_4, - // int V_5) - // IL_0000: ldarg.0 - // IL_0001: ldc.i4.0 - // IL_0002: ldelema "int" - // IL_0007: dup - // IL_0008: ldind.i4 - // IL_0009: stloc.0 - // IL_000a: call "System.Threading.Tasks.Task Test.G()" - // IL_000f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0014: stloc.1 - // IL_0015: ldloc.0 - // IL_0016: ldloc.1 - // IL_0017: add - // IL_0018: dup - // IL_0019: stloc.3 - // IL_001a: stind.i4 - // IL_001b: ldloc.3 - // IL_001c: call "bool Test.B(int)" - // IL_0021: stloc.2 - // IL_0022: ldloc.2 - // IL_0023: brtrue.s IL_004b - // IL_0025: ldarg.0 - // IL_0026: ldc.i4.1 - // IL_0027: ldelema "int" - // IL_002c: dup - // IL_002d: ldind.i4 - // IL_002e: stloc.3 - // IL_002f: call "System.Threading.Tasks.Task Test.G()" - // IL_0034: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0039: stloc.s V_4 - // IL_003b: ldloc.3 - // IL_003c: ldloc.s V_4 - // IL_003e: add - // IL_003f: dup - // IL_0040: stloc.s V_5 - // IL_0042: stind.i4 - // IL_0043: ldloc.s V_5 - // IL_0045: call "bool Test.B(int)" - // IL_004a: stloc.2 - // IL_004b: ldc.i4.0 - // IL_004c: ldloc.2 - // IL_004d: ldc.i4.4 - // IL_004e: call "int Test.H(int, bool, int)" - // IL_0053: pop - // IL_0054: ldc.i4.1 - // IL_0055: ret - // } - // """); + var verifier = CompileAndVerify(comp, verify: Verification.Fails with + { + ILVerifyMessage = """ + [F]: Unexpected type on the stack. { Offset = 0x5f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Test.F(int[])", """ + { + // Code size 96 (0x60) + .maxstack 3 + .locals init (int V_0, + int V_1, + bool V_2, + int V_3, + int V_4, + int V_5) + IL_0000: ldarg.0 + IL_0001: dup + IL_0002: ldc.i4.0 + IL_0003: ldelem.i4 + IL_0004: pop + IL_0005: dup + IL_0006: ldc.i4.0 + IL_0007: ldelem.i4 + IL_0008: stloc.0 + IL_0009: call "System.Threading.Tasks.Task Test.G()" + IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0013: stloc.1 + IL_0014: ldc.i4.0 + IL_0015: ldelema "int" + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: add + IL_001d: dup + IL_001e: stloc.3 + IL_001f: stind.i4 + IL_0020: ldloc.3 + IL_0021: call "bool Test.B(int)" + IL_0026: stloc.2 + IL_0027: ldloc.2 + IL_0028: brtrue.s IL_0055 + IL_002a: ldarg.0 + IL_002b: dup + IL_002c: ldc.i4.1 + IL_002d: ldelem.i4 + IL_002e: pop + IL_002f: dup + IL_0030: ldc.i4.1 + IL_0031: ldelem.i4 + IL_0032: stloc.3 + IL_0033: call "System.Threading.Tasks.Task Test.G()" + IL_0038: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003d: stloc.s V_4 + IL_003f: ldc.i4.1 + IL_0040: ldelema "int" + IL_0045: ldloc.3 + IL_0046: ldloc.s V_4 + IL_0048: add + IL_0049: dup + IL_004a: stloc.s V_5 + IL_004c: stind.i4 + IL_004d: ldloc.s V_5 + IL_004f: call "bool Test.B(int)" + IL_0054: stloc.2 + IL_0055: ldc.i4.0 + IL_0056: ldloc.2 + IL_0057: ldc.i4.4 + IL_0058: call "int Test.H(int, bool, int)" + IL_005d: pop + IL_005e: ldc.i4.1 + IL_005f: ret + } + """); } [Fact] @@ -2929,81 +2959,80 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[0] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run(T)").WithLocation(23, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x56 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 87 (0x57) - // .maxstack 4 - // .locals init (int V_0, //tests - // int V_1, - // int V_2) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldc.i4.4 - // IL_0003: newarr "int" - // IL_0008: dup - // IL_0009: ldc.i4.0 - // IL_000a: ldc.i4.4 - // IL_000b: stelem.i4 - // IL_000c: ldloc.0 - // IL_000d: ldc.i4.1 - // IL_000e: add - // IL_000f: stloc.0 - // IL_0010: dup - // IL_0011: ldc.i4.0 - // IL_0012: ldelema "int" - // IL_0017: dup - // IL_0018: ldind.i4 - // IL_0019: stloc.1 - // IL_001a: ldarg.0 - // IL_001b: ldc.i4.4 - // IL_001c: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0026: stloc.2 - // IL_0027: ldloc.1 - // IL_0028: ldloc.2 - // IL_0029: add - // IL_002a: stind.i4 - // IL_002b: ldc.i4.0 - // IL_002c: ldelem.i4 - // IL_002d: ldc.i4.8 - // IL_002e: bne.un.s IL_003c - // IL_0030: ldsfld "int Driver.Count" - // IL_0035: ldc.i4.1 - // IL_0036: add - // IL_0037: stsfld "int Driver.Count" - // IL_003c: leave.s IL_0056 - // } - // finally - // { - // IL_003e: ldsfld "int Driver.Count" - // IL_0043: ldloc.0 - // IL_0044: sub - // IL_0045: stsfld "int Driver.Result" - // IL_004a: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_004f: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0054: pop - // IL_0055: endfinally - // } - // IL_0056: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x5b } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 92 (0x5c) + .maxstack 4 + .locals init (int V_0, //tests + int V_1, + int V_2) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.4 + IL_000b: stelem.i4 + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: add + IL_000f: stloc.0 + IL_0010: dup + IL_0011: dup + IL_0012: ldc.i4.0 + IL_0013: ldelem.i4 + IL_0014: pop + IL_0015: dup + IL_0016: ldc.i4.0 + IL_0017: ldelem.i4 + IL_0018: stloc.1 + IL_0019: ldarg.0 + IL_001a: ldc.i4.4 + IL_001b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.2 + IL_0026: ldc.i4.0 + IL_0027: ldelema "int" + IL_002c: ldloc.1 + IL_002d: ldloc.2 + IL_002e: add + IL_002f: stind.i4 + IL_0030: ldc.i4.0 + IL_0031: ldelem.i4 + IL_0032: ldc.i4.8 + IL_0033: bne.un.s IL_0041 + IL_0035: ldsfld "int Driver.Count" + IL_003a: ldc.i4.1 + IL_003b: add + IL_003c: stsfld "int Driver.Count" + IL_0041: leave.s IL_005b + } + finally + { + IL_0043: ldsfld "int Driver.Count" + IL_0048: ldloc.0 + IL_0049: sub + IL_004a: stsfld "int Driver.Result" + IL_004f: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0054: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0059: pop + IL_005a: endfinally + } + IL_005b: ret + } + """); } [Fact] @@ -3128,86 +3157,85 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (22,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1] += await (GetVal(arr[0])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0]))").WithArguments("TestCase.Run(T)").WithLocation(22, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x5a } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 91 (0x5b) - // .maxstack 4 - // .locals init (int V_0, //tests - // int[] V_1, //arr - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldc.i4.4 - // IL_0003: newarr "int" - // IL_0008: dup - // IL_0009: ldc.i4.0 - // IL_000a: ldc.i4.8 - // IL_000b: stelem.i4 - // IL_000c: stloc.1 - // IL_000d: ldloc.0 - // IL_000e: ldc.i4.1 - // IL_000f: add - // IL_0010: stloc.0 - // IL_0011: ldloc.1 - // IL_0012: ldc.i4.1 - // IL_0013: ldelema "int" - // IL_0018: dup - // IL_0019: ldind.i4 - // IL_001a: stloc.2 - // IL_001b: ldarg.0 - // IL_001c: ldloc.1 - // IL_001d: ldc.i4.0 - // IL_001e: ldelem.i4 - // IL_001f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0024: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0029: stloc.3 - // IL_002a: ldloc.2 - // IL_002b: ldloc.3 - // IL_002c: add - // IL_002d: stind.i4 - // IL_002e: ldloc.1 - // IL_002f: ldc.i4.1 - // IL_0030: ldelem.i4 - // IL_0031: ldc.i4.8 - // IL_0032: bne.un.s IL_0040 - // IL_0034: ldsfld "int Driver.Count" - // IL_0039: ldc.i4.1 - // IL_003a: add - // IL_003b: stsfld "int Driver.Count" - // IL_0040: leave.s IL_005a - // } - // finally - // { - // IL_0042: ldsfld "int Driver.Count" - // IL_0047: ldloc.0 - // IL_0048: sub - // IL_0049: stsfld "int Driver.Result" - // IL_004e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0053: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0058: pop - // IL_0059: endfinally - // } - // IL_005a: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x5f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 96 (0x60) + .maxstack 4 + .locals init (int V_0, //tests + int[] V_1, //arr + int V_2, + int V_3) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.8 + IL_000b: stelem.i4 + IL_000c: stloc.1 + IL_000d: ldloc.0 + IL_000e: ldc.i4.1 + IL_000f: add + IL_0010: stloc.0 + IL_0011: ldloc.1 + IL_0012: dup + IL_0013: ldc.i4.1 + IL_0014: ldelem.i4 + IL_0015: pop + IL_0016: dup + IL_0017: ldc.i4.1 + IL_0018: ldelem.i4 + IL_0019: stloc.2 + IL_001a: ldarg.0 + IL_001b: ldloc.1 + IL_001c: ldc.i4.0 + IL_001d: ldelem.i4 + IL_001e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.3 + IL_0029: ldc.i4.1 + IL_002a: ldelema "int" + IL_002f: ldloc.2 + IL_0030: ldloc.3 + IL_0031: add + IL_0032: stind.i4 + IL_0033: ldloc.1 + IL_0034: ldc.i4.1 + IL_0035: ldelem.i4 + IL_0036: ldc.i4.8 + IL_0037: bne.un.s IL_0045 + IL_0039: ldsfld "int Driver.Count" + IL_003e: ldc.i4.1 + IL_003f: add + IL_0040: stsfld "int Driver.Count" + IL_0045: leave.s IL_005f + } + finally + { + IL_0047: ldsfld "int Driver.Count" + IL_004c: ldloc.0 + IL_004d: sub + IL_004e: stsfld "int Driver.Result" + IL_0053: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_0058: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_005d: pop + IL_005e: endfinally + } + IL_005f: ret + } + """); } [Fact] @@ -3342,127 +3370,126 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (22,23): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1] += await (GetVal(arr[await GetVal(0)])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[await GetVal(0)]))").WithArguments("TestCase.Run(T)").WithLocation(22, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0xa3 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 164 (0xa4) - // .maxstack 4 - // .locals init (int V_0, //tests - // int& V_1, - // int V_2, - // int V_3, - // int[] V_4, - // int V_5) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldc.i4.4 - // IL_0003: newarr "int" - // IL_0008: dup - // IL_0009: ldc.i4.0 - // IL_000a: ldc.i4.8 - // IL_000b: stelem.i4 - // IL_000c: dup - // IL_000d: ldc.i4.1 - // IL_000e: ldc.i4.8 - // IL_000f: stelem.i4 - // IL_0010: ldloc.0 - // IL_0011: ldc.i4.1 - // IL_0012: add - // IL_0013: stloc.0 - // IL_0014: dup - // IL_0015: ldc.i4.1 - // IL_0016: ldelema "int" - // IL_001b: stloc.1 - // IL_001c: ldloc.1 - // IL_001d: ldind.i4 - // IL_001e: stloc.2 - // IL_001f: dup - // IL_0020: stloc.s V_4 - // IL_0022: ldarg.0 - // IL_0023: ldc.i4.0 - // IL_0024: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0029: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_002e: stloc.s V_5 - // IL_0030: ldarg.0 - // IL_0031: ldloc.s V_4 - // IL_0033: ldloc.s V_5 - // IL_0035: ldelem.i4 - // IL_0036: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_003b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0040: stloc.3 - // IL_0041: ldloc.1 - // IL_0042: ldloc.2 - // IL_0043: ldloc.3 - // IL_0044: add - // IL_0045: stind.i4 - // IL_0046: dup - // IL_0047: ldc.i4.1 - // IL_0048: ldelem.i4 - // IL_0049: ldc.i4.s 16 - // IL_004b: bne.un.s IL_0059 - // IL_004d: ldsfld "int Driver.Count" - // IL_0052: ldc.i4.1 - // IL_0053: add - // IL_0054: stsfld "int Driver.Count" - // IL_0059: ldloc.0 - // IL_005a: ldc.i4.1 - // IL_005b: add - // IL_005c: stloc.0 - // IL_005d: dup - // IL_005e: ldarg.0 - // IL_005f: ldc.i4.2 - // IL_0060: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0065: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_006a: stloc.3 - // IL_006b: ldloc.3 - // IL_006c: ldelema "int" - // IL_0071: dup - // IL_0072: ldind.i4 - // IL_0073: stloc.2 - // IL_0074: ldloc.2 - // IL_0075: ldc.i4.1 - // IL_0076: add - // IL_0077: stind.i4 - // IL_0078: ldc.i4.2 - // IL_0079: ldelem.i4 - // IL_007a: ldc.i4.1 - // IL_007b: bne.un.s IL_0089 - // IL_007d: ldsfld "int Driver.Count" - // IL_0082: ldc.i4.1 - // IL_0083: add - // IL_0084: stsfld "int Driver.Count" - // IL_0089: leave.s IL_00a3 - // } - // finally - // { - // IL_008b: ldsfld "int Driver.Count" - // IL_0090: ldloc.0 - // IL_0091: sub - // IL_0092: stsfld "int Driver.Result" - // IL_0097: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_009c: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_00a1: pop - // IL_00a2: endfinally - // } - // IL_00a3: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0xa8 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 169 (0xa9) + .maxstack 4 + .locals init (int[] V_0, + int V_1, //tests + int V_2, + int V_3, + int[] V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.1 + .try + { + IL_0002: ldc.i4.4 + IL_0003: newarr "int" + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldc.i4.8 + IL_000b: stelem.i4 + IL_000c: dup + IL_000d: ldc.i4.1 + IL_000e: ldc.i4.8 + IL_000f: stelem.i4 + IL_0010: ldloc.1 + IL_0011: ldc.i4.1 + IL_0012: add + IL_0013: stloc.1 + IL_0014: dup + IL_0015: stloc.0 + IL_0016: ldloc.0 + IL_0017: ldc.i4.1 + IL_0018: ldelem.i4 + IL_0019: pop + IL_001a: ldloc.0 + IL_001b: ldc.i4.1 + IL_001c: ldelem.i4 + IL_001d: stloc.2 + IL_001e: dup + IL_001f: stloc.s V_4 + IL_0021: ldarg.0 + IL_0022: ldc.i4.0 + IL_0023: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0028: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002d: stloc.s V_5 + IL_002f: ldarg.0 + IL_0030: ldloc.s V_4 + IL_0032: ldloc.s V_5 + IL_0034: ldelem.i4 + IL_0035: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_003a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003f: stloc.3 + IL_0040: ldloc.0 + IL_0041: ldc.i4.1 + IL_0042: ldelema "int" + IL_0047: ldloc.2 + IL_0048: ldloc.3 + IL_0049: add + IL_004a: stind.i4 + IL_004b: dup + IL_004c: ldc.i4.1 + IL_004d: ldelem.i4 + IL_004e: ldc.i4.s 16 + IL_0050: bne.un.s IL_005e + IL_0052: ldsfld "int Driver.Count" + IL_0057: ldc.i4.1 + IL_0058: add + IL_0059: stsfld "int Driver.Count" + IL_005e: ldloc.1 + IL_005f: ldc.i4.1 + IL_0060: add + IL_0061: stloc.1 + IL_0062: dup + IL_0063: ldarg.0 + IL_0064: ldc.i4.2 + IL_0065: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_006a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_006f: stloc.3 + IL_0070: ldloc.3 + IL_0071: ldelema "int" + IL_0076: dup + IL_0077: ldind.i4 + IL_0078: stloc.2 + IL_0079: ldloc.2 + IL_007a: ldc.i4.1 + IL_007b: add + IL_007c: stind.i4 + IL_007d: ldc.i4.2 + IL_007e: ldelem.i4 + IL_007f: ldc.i4.1 + IL_0080: bne.un.s IL_008e + IL_0082: ldsfld "int Driver.Count" + IL_0087: ldc.i4.1 + IL_0088: add + IL_0089: stsfld "int Driver.Count" + IL_008e: leave.s IL_00a8 + } + finally + { + IL_0090: ldsfld "int Driver.Count" + IL_0095: ldloc.1 + IL_0096: sub + IL_0097: stsfld "int Driver.Result" + IL_009c: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_00a1: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_00a6: pop + IL_00a7: endfinally + } + IL_00a8: ret + } + """); } [Fact] @@ -3816,239 +3843,248 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (27,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[0, 0] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run(T)").WithLocation(27, 26), - // (32,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1, 1] += await (GetVal(arr[0, 0])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0, 0]))").WithArguments("TestCase.Run(T)").WithLocation(32, 26), - // (37,26): error CS9328: Method 'TestCase.Run(T)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[1, 1] += await (GetVal(arr[0, await GetVal(0)])); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await (GetVal(arr[0, await GetVal(0)]))").WithArguments("TestCase.Run(T)").WithLocation(37, 26) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x178 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run(T)", """ - // { - // // Code size 377 (0x179) - // .maxstack 5 - // .locals init (int V_0, //tests - // int[,] V_1, //arr - // int V_2, - // int V_3, - // int[,] V_4, - // int V_5) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldarg.0 - // IL_0003: ldc.i4.4 - // IL_0004: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000e: ldarg.0 - // IL_000f: ldc.i4.4 - // IL_0010: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_001a: stloc.2 - // IL_001b: ldloc.2 - // IL_001c: newobj "int[*,*]..ctor" - // IL_0021: stloc.1 - // IL_0022: ldloc.0 - // IL_0023: ldc.i4.1 - // IL_0024: add - // IL_0025: stloc.0 - // IL_0026: ldloc.1 - // IL_0027: ldarg.0 - // IL_0028: ldc.i4.4 - // IL_0029: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_002e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0033: stloc.2 - // IL_0034: ldc.i4.0 - // IL_0035: ldc.i4.0 - // IL_0036: ldloc.2 - // IL_0037: call "int[*,*].Set" - // IL_003c: ldloc.1 - // IL_003d: ldarg.0 - // IL_003e: ldc.i4.0 - // IL_003f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0044: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0049: stloc.2 - // IL_004a: ldc.i4.0 - // IL_004b: ldloc.2 - // IL_004c: call "int[*,*].Get" - // IL_0051: ldc.i4.4 - // IL_0052: bne.un.s IL_0060 - // IL_0054: ldsfld "int Driver.Count" - // IL_0059: ldc.i4.1 - // IL_005a: add - // IL_005b: stsfld "int Driver.Count" - // IL_0060: ldloc.0 - // IL_0061: ldc.i4.1 - // IL_0062: add - // IL_0063: stloc.0 - // IL_0064: ldloc.1 - // IL_0065: ldc.i4.0 - // IL_0066: ldc.i4.0 - // IL_0067: call "int[*,*].Address" - // IL_006c: dup - // IL_006d: ldind.i4 - // IL_006e: stloc.2 - // IL_006f: ldarg.0 - // IL_0070: ldc.i4.4 - // IL_0071: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0076: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_007b: stloc.3 - // IL_007c: ldloc.2 - // IL_007d: ldloc.3 - // IL_007e: add - // IL_007f: stind.i4 - // IL_0080: ldloc.1 - // IL_0081: ldc.i4.0 - // IL_0082: ldc.i4.0 - // IL_0083: call "int[*,*].Get" - // IL_0088: ldc.i4.8 - // IL_0089: bne.un.s IL_0097 - // IL_008b: ldsfld "int Driver.Count" - // IL_0090: ldc.i4.1 - // IL_0091: add - // IL_0092: stsfld "int Driver.Count" - // IL_0097: ldloc.0 - // IL_0098: ldc.i4.1 - // IL_0099: add - // IL_009a: stloc.0 - // IL_009b: ldloc.1 - // IL_009c: ldc.i4.1 - // IL_009d: ldc.i4.1 - // IL_009e: call "int[*,*].Address" - // IL_00a3: dup - // IL_00a4: ldind.i4 - // IL_00a5: stloc.3 - // IL_00a6: ldarg.0 - // IL_00a7: ldloc.1 - // IL_00a8: ldc.i4.0 - // IL_00a9: ldc.i4.0 - // IL_00aa: call "int[*,*].Get" - // IL_00af: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00b4: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00b9: stloc.2 - // IL_00ba: ldloc.3 - // IL_00bb: ldloc.2 - // IL_00bc: add - // IL_00bd: stind.i4 - // IL_00be: ldloc.1 - // IL_00bf: ldc.i4.1 - // IL_00c0: ldc.i4.1 - // IL_00c1: call "int[*,*].Get" - // IL_00c6: ldc.i4.8 - // IL_00c7: bne.un.s IL_00d5 - // IL_00c9: ldsfld "int Driver.Count" - // IL_00ce: ldc.i4.1 - // IL_00cf: add - // IL_00d0: stsfld "int Driver.Count" - // IL_00d5: ldloc.0 - // IL_00d6: ldc.i4.1 - // IL_00d7: add - // IL_00d8: stloc.0 - // IL_00d9: ldloc.1 - // IL_00da: ldc.i4.1 - // IL_00db: ldc.i4.1 - // IL_00dc: call "int[*,*].Address" - // IL_00e1: dup - // IL_00e2: ldind.i4 - // IL_00e3: stloc.2 - // IL_00e4: ldloc.1 - // IL_00e5: stloc.s V_4 - // IL_00e7: ldarg.0 - // IL_00e8: ldc.i4.0 - // IL_00e9: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00ee: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00f3: stloc.s V_5 - // IL_00f5: ldarg.0 - // IL_00f6: ldloc.s V_4 - // IL_00f8: ldc.i4.0 - // IL_00f9: ldloc.s V_5 - // IL_00fb: call "int[*,*].Get" - // IL_0100: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0105: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_010a: stloc.3 - // IL_010b: ldloc.2 - // IL_010c: ldloc.3 - // IL_010d: add - // IL_010e: stind.i4 - // IL_010f: ldloc.1 - // IL_0110: ldc.i4.1 - // IL_0111: ldc.i4.1 - // IL_0112: call "int[*,*].Get" - // IL_0117: ldc.i4.s 16 - // IL_0119: bne.un.s IL_0127 - // IL_011b: ldsfld "int Driver.Count" - // IL_0120: ldc.i4.1 - // IL_0121: add - // IL_0122: stsfld "int Driver.Count" - // IL_0127: ldloc.0 - // IL_0128: ldc.i4.1 - // IL_0129: add - // IL_012a: stloc.0 - // IL_012b: ldloc.1 - // IL_012c: ldarg.0 - // IL_012d: ldc.i4.2 - // IL_012e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0133: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0138: stloc.3 - // IL_0139: ldc.i4.2 - // IL_013a: ldloc.3 - // IL_013b: call "int[*,*].Address" - // IL_0140: dup - // IL_0141: ldind.i4 - // IL_0142: stloc.2 - // IL_0143: ldloc.2 - // IL_0144: ldc.i4.1 - // IL_0145: add - // IL_0146: stind.i4 - // IL_0147: ldloc.1 - // IL_0148: ldc.i4.2 - // IL_0149: ldc.i4.2 - // IL_014a: call "int[*,*].Get" - // IL_014f: ldc.i4.1 - // IL_0150: bne.un.s IL_015e - // IL_0152: ldsfld "int Driver.Count" - // IL_0157: ldc.i4.1 - // IL_0158: add - // IL_0159: stsfld "int Driver.Count" - // IL_015e: leave.s IL_0178 - // } - // finally - // { - // IL_0160: ldsfld "int Driver.Count" - // IL_0165: ldloc.0 - // IL_0166: sub - // IL_0167: stsfld "int Driver.Result" - // IL_016c: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0171: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0176: pop - // IL_0177: endfinally - // } - // IL_0178: ret - // } - // """); - } + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x1a5 } + """ + }); - [Fact] - public void SpillArray04() - { - var source = @" -using System.Threading; -using System.Threading.Tasks; + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run(T)", """ + { + // Code size 422 (0x1a6) + .maxstack 5 + .locals init (int V_0, //tests + int[,] V_1, //arr + int V_2, + int V_3, + int[,] V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + .try + { + IL_0002: ldarg.0 + IL_0003: ldc.i4.4 + IL_0004: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0009: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000e: ldarg.0 + IL_000f: ldc.i4.4 + IL_0010: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0015: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001a: stloc.2 + IL_001b: ldloc.2 + IL_001c: newobj "int[*,*]..ctor" + IL_0021: stloc.1 + IL_0022: ldloc.0 + IL_0023: ldc.i4.1 + IL_0024: add + IL_0025: stloc.0 + IL_0026: ldloc.1 + IL_0027: ldarg.0 + IL_0028: ldc.i4.4 + IL_0029: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_002e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0033: stloc.2 + IL_0034: ldc.i4.0 + IL_0035: ldc.i4.0 + IL_0036: ldloc.2 + IL_0037: call "int[*,*].Set" + IL_003c: ldloc.1 + IL_003d: ldarg.0 + IL_003e: ldc.i4.0 + IL_003f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0044: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0049: stloc.2 + IL_004a: ldc.i4.0 + IL_004b: ldloc.2 + IL_004c: call "int[*,*].Get" + IL_0051: ldc.i4.4 + IL_0052: bne.un.s IL_0060 + IL_0054: ldsfld "int Driver.Count" + IL_0059: ldc.i4.1 + IL_005a: add + IL_005b: stsfld "int Driver.Count" + IL_0060: ldloc.0 + IL_0061: ldc.i4.1 + IL_0062: add + IL_0063: stloc.0 + IL_0064: ldloc.1 + IL_0065: dup + IL_0066: ldc.i4.0 + IL_0067: ldc.i4.0 + IL_0068: call "int[*,*].Get" + IL_006d: pop + IL_006e: dup + IL_006f: ldc.i4.0 + IL_0070: ldc.i4.0 + IL_0071: call "int[*,*].Get" + IL_0076: stloc.2 + IL_0077: ldarg.0 + IL_0078: ldc.i4.4 + IL_0079: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_007e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0083: stloc.3 + IL_0084: ldc.i4.0 + IL_0085: ldc.i4.0 + IL_0086: call "int[*,*].Address" + IL_008b: ldloc.2 + IL_008c: ldloc.3 + IL_008d: add + IL_008e: stind.i4 + IL_008f: ldloc.1 + IL_0090: ldc.i4.0 + IL_0091: ldc.i4.0 + IL_0092: call "int[*,*].Get" + IL_0097: ldc.i4.8 + IL_0098: bne.un.s IL_00a6 + IL_009a: ldsfld "int Driver.Count" + IL_009f: ldc.i4.1 + IL_00a0: add + IL_00a1: stsfld "int Driver.Count" + IL_00a6: ldloc.0 + IL_00a7: ldc.i4.1 + IL_00a8: add + IL_00a9: stloc.0 + IL_00aa: ldloc.1 + IL_00ab: dup + IL_00ac: ldc.i4.1 + IL_00ad: ldc.i4.1 + IL_00ae: call "int[*,*].Get" + IL_00b3: pop + IL_00b4: dup + IL_00b5: ldc.i4.1 + IL_00b6: ldc.i4.1 + IL_00b7: call "int[*,*].Get" + IL_00bc: stloc.3 + IL_00bd: ldarg.0 + IL_00be: ldloc.1 + IL_00bf: ldc.i4.0 + IL_00c0: ldc.i4.0 + IL_00c1: call "int[*,*].Get" + IL_00c6: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00cb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00d0: stloc.2 + IL_00d1: ldc.i4.1 + IL_00d2: ldc.i4.1 + IL_00d3: call "int[*,*].Address" + IL_00d8: ldloc.3 + IL_00d9: ldloc.2 + IL_00da: add + IL_00db: stind.i4 + IL_00dc: ldloc.1 + IL_00dd: ldc.i4.1 + IL_00de: ldc.i4.1 + IL_00df: call "int[*,*].Get" + IL_00e4: ldc.i4.8 + IL_00e5: bne.un.s IL_00f3 + IL_00e7: ldsfld "int Driver.Count" + IL_00ec: ldc.i4.1 + IL_00ed: add + IL_00ee: stsfld "int Driver.Count" + IL_00f3: ldloc.0 + IL_00f4: ldc.i4.1 + IL_00f5: add + IL_00f6: stloc.0 + IL_00f7: ldloc.1 + IL_00f8: dup + IL_00f9: ldc.i4.1 + IL_00fa: ldc.i4.1 + IL_00fb: call "int[*,*].Get" + IL_0100: pop + IL_0101: dup + IL_0102: ldc.i4.1 + IL_0103: ldc.i4.1 + IL_0104: call "int[*,*].Get" + IL_0109: stloc.2 + IL_010a: ldloc.1 + IL_010b: stloc.s V_4 + IL_010d: ldarg.0 + IL_010e: ldc.i4.0 + IL_010f: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0114: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0119: stloc.s V_5 + IL_011b: ldarg.0 + IL_011c: ldloc.s V_4 + IL_011e: ldc.i4.0 + IL_011f: ldloc.s V_5 + IL_0121: call "int[*,*].Get" + IL_0126: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_012b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0130: stloc.3 + IL_0131: ldc.i4.1 + IL_0132: ldc.i4.1 + IL_0133: call "int[*,*].Address" + IL_0138: ldloc.2 + IL_0139: ldloc.3 + IL_013a: add + IL_013b: stind.i4 + IL_013c: ldloc.1 + IL_013d: ldc.i4.1 + IL_013e: ldc.i4.1 + IL_013f: call "int[*,*].Get" + IL_0144: ldc.i4.s 16 + IL_0146: bne.un.s IL_0154 + IL_0148: ldsfld "int Driver.Count" + IL_014d: ldc.i4.1 + IL_014e: add + IL_014f: stsfld "int Driver.Count" + IL_0154: ldloc.0 + IL_0155: ldc.i4.1 + IL_0156: add + IL_0157: stloc.0 + IL_0158: ldloc.1 + IL_0159: ldarg.0 + IL_015a: ldc.i4.2 + IL_015b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0160: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0165: stloc.3 + IL_0166: ldc.i4.2 + IL_0167: ldloc.3 + IL_0168: call "int[*,*].Address" + IL_016d: dup + IL_016e: ldind.i4 + IL_016f: stloc.2 + IL_0170: ldloc.2 + IL_0171: ldc.i4.1 + IL_0172: add + IL_0173: stind.i4 + IL_0174: ldloc.1 + IL_0175: ldc.i4.2 + IL_0176: ldc.i4.2 + IL_0177: call "int[*,*].Get" + IL_017c: ldc.i4.1 + IL_017d: bne.un.s IL_018b + IL_017f: ldsfld "int Driver.Count" + IL_0184: ldc.i4.1 + IL_0185: add + IL_0186: stsfld "int Driver.Count" + IL_018b: leave.s IL_01a5 + } + finally + { + IL_018d: ldsfld "int Driver.Count" + IL_0192: ldloc.0 + IL_0193: sub + IL_0194: stsfld "int Driver.Result" + IL_0199: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_019e: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_01a3: pop + IL_01a4: endfinally + } + IL_01a5: ret + } + """); + } + + [Fact] + public void SpillArray04() + { + var source = @" +using System.Threading; +using System.Threading.Tasks; struct MyStruct { @@ -4159,51 +4195,49 @@ static void Main() CompileAndVerify(source, ""); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (23,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Run() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(23, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x2b } - // [Goo]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 44 (0x2c) - // .maxstack 2 - // .locals init (MyStruct V_0, //ms - // int V_1) - // .try - // { - // IL_0000: ldloca.s V_0 - // IL_0002: initobj "MyStruct" - // IL_0008: ldarg.0 - // IL_0009: call "System.Threading.Tasks.Task TestCase.Goo()" - // IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0013: stloc.1 - // IL_0014: ldloca.s V_0 - // IL_0016: ldloc.1 - // IL_0017: call "int MyStruct.this[int].get" - // IL_001c: pop - // IL_001d: leave.s IL_002b - // } - // finally - // { - // IL_001f: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0024: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0029: pop - // IL_002a: endfinally - // } - // IL_002b: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x33 } + [Goo]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 52 (0x34) + .maxstack 2 + .locals init (TestCase V_0, + MyStruct V_1, //ms + int V_2) + IL_0000: ldarg.0 + IL_0001: ldobj "TestCase" + IL_0006: stloc.0 + .try + { + IL_0007: ldloca.s V_1 + IL_0009: initobj "MyStruct" + IL_000f: ldloca.s V_0 + IL_0011: call "System.Threading.Tasks.Task TestCase.Goo()" + IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.2 + IL_001c: ldloca.s V_1 + IL_001e: ldloc.2 + IL_001f: call "int MyStruct.this[int].get" + IL_0024: pop + IL_0025: leave.s IL_0033 + } + finally + { + IL_0027: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_002c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0031: pop + IL_0032: endfinally + } + IL_0033: ret + } + """); } [Fact] @@ -4275,7 +4309,6 @@ .locals init (int V_0) public void SpillArrayAssign2() { var source = @" -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously using System.Threading.Tasks; class Program @@ -4599,55 +4632,54 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (12,19): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[0] += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 42)").WithArguments("Driver.Run()").WithLocation(12, 19) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x4c } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 77 (0x4d) - // .maxstack 4 - // .locals init (int V_0, - // int V_1) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "int" - // IL_0006: stsfld "int[] Driver.arr" - // IL_000b: ldsfld "int[] Driver.arr" - // IL_0010: ldc.i4.0 - // IL_0011: ldelema "int" - // IL_0016: dup - // IL_0017: ldind.i4 - // IL_0018: stloc.0 - // IL_0019: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_001e: ldsfld "System.Func Driver.<>c.<>9__1_0" - // IL_0023: dup - // IL_0024: brtrue.s IL_003d - // IL_0026: pop - // IL_0027: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_002c: ldftn "int Driver.<>c.b__1_0()" - // IL_0032: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0037: dup - // IL_0038: stsfld "System.Func Driver.<>c.<>9__1_0" - // IL_003d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0047: stloc.1 - // IL_0048: ldloc.0 - // IL_0049: ldloc.1 - // IL_004a: add - // IL_004b: stind.i4 - // IL_004c: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x51 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 82 (0x52) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: stsfld "int[] Driver.arr" + IL_000b: ldsfld "int[] Driver.arr" + IL_0010: dup + IL_0011: ldc.i4.0 + IL_0012: ldelem.i4 + IL_0013: pop + IL_0014: dup + IL_0015: ldc.i4.0 + IL_0016: ldelem.i4 + IL_0017: stloc.0 + IL_0018: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_001d: ldsfld "System.Func Driver.<>c.<>9__1_0" + IL_0022: dup + IL_0023: brtrue.s IL_003c + IL_0025: pop + IL_0026: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_002b: ldftn "int Driver.<>c.b__1_0()" + IL_0031: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0036: dup + IL_0037: stsfld "System.Func Driver.<>c.<>9__1_0" + IL_003c: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0041: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0046: stloc.1 + IL_0047: ldc.i4.0 + IL_0048: ldelema "int" + IL_004d: ldloc.0 + IL_004e: ldloc.1 + IL_004f: add + IL_0050: stind.i4 + IL_0051: ret + } + """); } [Fact] @@ -4679,69 +4711,71 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (12,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)] += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(12, 13) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x7b } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 124 (0x7c) - // .maxstack 4 - // .locals init (int V_0, - // int V_1, - // int V_2) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "int" - // IL_0006: stsfld "int[] Driver.arr" - // IL_000b: ldsfld "int[] Driver.arr" - // IL_0010: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0015: ldsfld "System.Func Driver.<>c.<>9__1_0" - // IL_001a: dup - // IL_001b: brtrue.s IL_0034 - // IL_001d: pop - // IL_001e: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_0023: ldftn "int Driver.<>c.b__1_0()" - // IL_0029: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_002e: dup - // IL_002f: stsfld "System.Func Driver.<>c.<>9__1_0" - // IL_0034: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003e: stloc.0 - // IL_003f: ldloc.0 - // IL_0040: ldelema "int" - // IL_0045: dup - // IL_0046: ldind.i4 - // IL_0047: stloc.1 - // IL_0048: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_004d: ldsfld "System.Func Driver.<>c.<>9__1_1" - // IL_0052: dup - // IL_0053: brtrue.s IL_006c - // IL_0055: pop - // IL_0056: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_005b: ldftn "int Driver.<>c.b__1_1()" - // IL_0061: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0066: dup - // IL_0067: stsfld "System.Func Driver.<>c.<>9__1_1" - // IL_006c: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0071: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0076: stloc.2 - // IL_0077: ldloc.1 - // IL_0078: ldloc.2 - // IL_0079: add - // IL_007a: stind.i4 - // IL_007b: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x82 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 131 (0x83) + .maxstack 4 + .locals init (int V_0, + int V_1, + int V_2, + int V_3) + IL_0000: ldc.i4.1 + IL_0001: newarr "int" + IL_0006: stsfld "int[] Driver.arr" + IL_000b: ldsfld "int[] Driver.arr" + IL_0010: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0015: ldsfld "System.Func Driver.<>c.<>9__1_0" + IL_001a: dup + IL_001b: brtrue.s IL_0034 + IL_001d: pop + IL_001e: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_0023: ldftn "int Driver.<>c.b__1_0()" + IL_0029: newobj "System.Func..ctor(object, System.IntPtr)" + IL_002e: dup + IL_002f: stsfld "System.Func Driver.<>c.<>9__1_0" + IL_0034: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0039: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003e: stloc.1 + IL_003f: ldloc.1 + IL_0040: stloc.0 + IL_0041: dup + IL_0042: ldloc.0 + IL_0043: ldelem.i4 + IL_0044: pop + IL_0045: dup + IL_0046: ldloc.0 + IL_0047: ldelem.i4 + IL_0048: stloc.2 + IL_0049: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_004e: ldsfld "System.Func Driver.<>c.<>9__1_1" + IL_0053: dup + IL_0054: brtrue.s IL_006d + IL_0056: pop + IL_0057: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_005c: ldftn "int Driver.<>c.b__1_1()" + IL_0062: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0067: dup + IL_0068: stsfld "System.Func Driver.<>c.<>9__1_1" + IL_006d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0077: stloc.3 + IL_0078: ldloc.0 + IL_0079: ldelema "int" + IL_007e: ldloc.2 + IL_007f: ldloc.3 + IL_0080: add + IL_0081: stind.i4 + IL_0082: ret + } + """); } [Fact] @@ -4783,90 +4817,98 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13), - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13) - ); - // https://github.com/dotnet/roslyn/issues/79763 - support struct lifting - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Unexpected type on the stack. { Offset = 0xbb, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 188 (0xbc) - // .maxstack 5 - // .locals init (int V_0, - // int V_1, - // int V_2) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "S2" - // IL_0006: dup - // IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" - // IL_0011: dup - // IL_0012: brtrue.s IL_002b - // IL_0014: pop - // IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_001a: ldftn "int Driver.<>c.b__0_0()" - // IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0025: dup - // IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" - // IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0035: stloc.0 - // IL_0036: ldloc.0 - // IL_0037: ldelema "S2" - // IL_003c: ldflda "S1 S2.s1" - // IL_0041: ldflda "int S1.x" - // IL_0046: dup - // IL_0047: ldind.i4 - // IL_0048: stloc.1 - // IL_0049: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_004e: ldsfld "System.Func Driver.<>c.<>9__0_1" - // IL_0053: dup - // IL_0054: brtrue.s IL_006d - // IL_0056: pop - // IL_0057: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_005c: ldftn "int Driver.<>c.b__0_1()" - // IL_0062: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0067: dup - // IL_0068: stsfld "System.Func Driver.<>c.<>9__0_1" - // IL_006d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0072: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0077: stloc.2 - // IL_0078: ldloc.1 - // IL_0079: ldloc.2 - // IL_007a: add - // IL_007b: stind.i4 - // IL_007c: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0081: ldsfld "System.Func Driver.<>c.<>9__0_2" - // IL_0086: dup - // IL_0087: brtrue.s IL_00a0 - // IL_0089: pop - // IL_008a: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_008f: ldftn "int Driver.<>c.b__0_2()" - // IL_0095: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_009a: dup - // IL_009b: stsfld "System.Func Driver.<>c.<>9__0_2" - // IL_00a0: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_00a5: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00aa: stloc.2 - // IL_00ab: ldloc.2 - // IL_00ac: ldelema "S2" - // IL_00b1: ldflda "S1 S2.s1" - // IL_00b6: ldfld "int S1.x" - // IL_00bb: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Unexpected type on the stack. { Offset = 0xe2, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 227 (0xe3) + .maxstack 5 + .locals init (int V_0, + int V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.1 + IL_0001: newarr "S2" + IL_0006: dup + IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" + IL_0011: dup + IL_0012: brtrue.s IL_002b + IL_0014: pop + IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_001a: ldftn "int Driver.<>c.b__0_0()" + IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0025: dup + IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" + IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0035: stloc.2 + IL_0036: ldloc.2 + IL_0037: stloc.0 + IL_0038: dup + IL_0039: ldloc.0 + IL_003a: ldelema "S2" + IL_003f: pop + IL_0040: ldloc.0 + IL_0041: stloc.1 + IL_0042: dup + IL_0043: ldloc.1 + IL_0044: ldelema "S2" + IL_0049: pop + IL_004a: dup + IL_004b: ldloc.1 + IL_004c: ldelema "S2" + IL_0051: ldflda "S1 S2.s1" + IL_0056: ldfld "int S1.x" + IL_005b: stloc.3 + IL_005c: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0061: ldsfld "System.Func Driver.<>c.<>9__0_1" + IL_0066: dup + IL_0067: brtrue.s IL_0080 + IL_0069: pop + IL_006a: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_006f: ldftn "int Driver.<>c.b__0_1()" + IL_0075: newobj "System.Func..ctor(object, System.IntPtr)" + IL_007a: dup + IL_007b: stsfld "System.Func Driver.<>c.<>9__0_1" + IL_0080: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0085: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_008a: stloc.s V_4 + IL_008c: ldloc.1 + IL_008d: ldelema "S2" + IL_0092: ldflda "S1 S2.s1" + IL_0097: ldflda "int S1.x" + IL_009c: ldloc.3 + IL_009d: ldloc.s V_4 + IL_009f: add + IL_00a0: stind.i4 + IL_00a1: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_00a6: ldsfld "System.Func Driver.<>c.<>9__0_2" + IL_00ab: dup + IL_00ac: brtrue.s IL_00c5 + IL_00ae: pop + IL_00af: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_00b4: ldftn "int Driver.<>c.b__0_2()" + IL_00ba: newobj "System.Func..ctor(object, System.IntPtr)" + IL_00bf: dup + IL_00c0: stsfld "System.Func Driver.<>c.<>9__0_2" + IL_00c5: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_00ca: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00cf: stloc.s V_4 + IL_00d1: ldloc.s V_4 + IL_00d3: ldelema "S2" + IL_00d8: ldflda "S1 S2.s1" + IL_00dd: ldfld "int S1.x" + IL_00e2: ret + } + """); } [Fact] @@ -4908,132 +4950,288 @@ static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (20,64): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 64), - // (20,64): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 64), - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13), - // (20,13): error CS9328: Method 'Driver.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // arr[await Task.Factory.StartNew(() => 0)].s1.x += (arr[await Task.Factory.StartNew(() => 0)].s1.x += await Task.Factory.StartNew(() => 42)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 0)").WithArguments("Driver.Run()").WithLocation(20, 13) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Unexpected type on the stack. { Offset = 0x113, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("Driver.Run()", """ - // { - // // Code size 276 (0x114) - // .maxstack 6 - // .locals init (int V_0, - // int& V_1, - // int V_2, - // int V_3, - // int& V_4, - // int V_5, - // int V_6, - // int V_7) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "S2" - // IL_0006: dup - // IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" - // IL_0011: dup - // IL_0012: brtrue.s IL_002b - // IL_0014: pop - // IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_001a: ldftn "int Driver.<>c.b__0_0()" - // IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0025: dup - // IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" - // IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0035: stloc.0 - // IL_0036: ldloc.0 - // IL_0037: ldelema "S2" - // IL_003c: ldflda "S1 S2.s1" - // IL_0041: ldflda "int S1.x" - // IL_0046: stloc.1 - // IL_0047: ldloc.1 - // IL_0048: ldind.i4 - // IL_0049: stloc.2 - // IL_004a: dup - // IL_004b: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0050: ldsfld "System.Func Driver.<>c.<>9__0_1" - // IL_0055: dup - // IL_0056: brtrue.s IL_006f - // IL_0058: pop - // IL_0059: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_005e: ldftn "int Driver.<>c.b__0_1()" - // IL_0064: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0069: dup - // IL_006a: stsfld "System.Func Driver.<>c.<>9__0_1" - // IL_006f: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0074: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0079: stloc.3 - // IL_007a: ldloc.3 - // IL_007b: ldelema "S2" - // IL_0080: ldflda "S1 S2.s1" - // IL_0085: ldflda "int S1.x" - // IL_008a: stloc.s V_4 - // IL_008c: ldloc.s V_4 - // IL_008e: ldind.i4 - // IL_008f: stloc.s V_5 - // IL_0091: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_0096: ldsfld "System.Func Driver.<>c.<>9__0_2" - // IL_009b: dup - // IL_009c: brtrue.s IL_00b5 - // IL_009e: pop - // IL_009f: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_00a4: ldftn "int Driver.<>c.b__0_2()" - // IL_00aa: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_00af: dup - // IL_00b0: stsfld "System.Func Driver.<>c.<>9__0_2" - // IL_00b5: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_00ba: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00bf: stloc.s V_6 - // IL_00c1: ldloc.1 - // IL_00c2: ldloc.2 - // IL_00c3: ldloc.s V_4 - // IL_00c5: ldloc.s V_5 - // IL_00c7: ldloc.s V_6 - // IL_00c9: add - // IL_00ca: dup - // IL_00cb: stloc.s V_7 - // IL_00cd: stind.i4 - // IL_00ce: ldloc.s V_7 - // IL_00d0: add - // IL_00d1: stind.i4 - // IL_00d2: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_00d7: ldsfld "System.Func Driver.<>c.<>9__0_3" - // IL_00dc: dup - // IL_00dd: brtrue.s IL_00f6 - // IL_00df: pop - // IL_00e0: ldsfld "Driver.<>c Driver.<>c.<>9" - // IL_00e5: ldftn "int Driver.<>c.b__0_3()" - // IL_00eb: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_00f0: dup - // IL_00f1: stsfld "System.Func Driver.<>c.<>9__0_3" - // IL_00f6: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_00fb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0100: stloc.s V_6 - // IL_0102: ldloc.s V_6 - // IL_0104: ldelema "S2" - // IL_0109: ldflda "S1 S2.s1" - // IL_010e: ldfld "int S1.x" - // IL_0113: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Unexpected type on the stack. { Offset = 0x168, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Driver.Run()", """ + { + // Code size 361 (0x169) + .maxstack 6 + .locals init (int V_0, + S2[] V_1, + int V_2, + int V_3, + S2[] V_4, + int V_5, + int V_6, + int V_7, + int V_8, + int& V_9, + int V_10, + int V_11, + int V_12) + IL_0000: ldc.i4.1 + IL_0001: newarr "S2" + IL_0006: dup + IL_0007: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000c: ldsfld "System.Func Driver.<>c.<>9__0_0" + IL_0011: dup + IL_0012: brtrue.s IL_002b + IL_0014: pop + IL_0015: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_001a: ldftn "int Driver.<>c.b__0_0()" + IL_0020: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0025: dup + IL_0026: stsfld "System.Func Driver.<>c.<>9__0_0" + IL_002b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0030: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0035: stloc.s V_6 + IL_0037: ldloc.s V_6 + IL_0039: stloc.0 + IL_003a: dup + IL_003b: ldloc.0 + IL_003c: ldelema "S2" + IL_0041: pop + IL_0042: stloc.1 + IL_0043: ldloc.0 + IL_0044: stloc.2 + IL_0045: ldloc.1 + IL_0046: ldloc.2 + IL_0047: ldelema "S2" + IL_004c: pop + IL_004d: ldloc.1 + IL_004e: ldloc.2 + IL_004f: ldelema "S2" + IL_0054: ldflda "S1 S2.s1" + IL_0059: ldfld "int S1.x" + IL_005e: stloc.s V_7 + IL_0060: dup + IL_0061: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_0066: ldsfld "System.Func Driver.<>c.<>9__0_1" + IL_006b: dup + IL_006c: brtrue.s IL_0085 + IL_006e: pop + IL_006f: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_0074: ldftn "int Driver.<>c.b__0_1()" + IL_007a: newobj "System.Func..ctor(object, System.IntPtr)" + IL_007f: dup + IL_0080: stsfld "System.Func Driver.<>c.<>9__0_1" + IL_0085: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_008a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_008f: stloc.s V_8 + IL_0091: ldloc.s V_8 + IL_0093: stloc.3 + IL_0094: dup + IL_0095: ldloc.3 + IL_0096: ldelema "S2" + IL_009b: pop + IL_009c: stloc.s V_4 + IL_009e: ldloc.3 + IL_009f: stloc.s V_5 + IL_00a1: ldloc.s V_4 + IL_00a3: ldloc.s V_5 + IL_00a5: ldelema "S2" + IL_00aa: pop + IL_00ab: ldloc.s V_4 + IL_00ad: ldloc.s V_5 + IL_00af: ldelema "S2" + IL_00b4: ldflda "S1 S2.s1" + IL_00b9: ldfld "int S1.x" + IL_00be: stloc.s V_10 + IL_00c0: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_00c5: ldsfld "System.Func Driver.<>c.<>9__0_2" + IL_00ca: dup + IL_00cb: brtrue.s IL_00e4 + IL_00cd: pop + IL_00ce: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_00d3: ldftn "int Driver.<>c.b__0_2()" + IL_00d9: newobj "System.Func..ctor(object, System.IntPtr)" + IL_00de: dup + IL_00df: stsfld "System.Func Driver.<>c.<>9__0_2" + IL_00e4: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_00e9: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00ee: stloc.s V_11 + IL_00f0: ldloc.1 + IL_00f1: ldloc.2 + IL_00f2: ldelema "S2" + IL_00f7: ldflda "S1 S2.s1" + IL_00fc: ldflda "int S1.x" + IL_0101: ldloc.s V_7 + IL_0103: ldloc.s V_4 + IL_0105: ldloc.s V_5 + IL_0107: ldelema "S2" + IL_010c: ldflda "S1 S2.s1" + IL_0111: ldflda "int S1.x" + IL_0116: stloc.s V_9 + IL_0118: ldloc.s V_9 + IL_011a: ldloc.s V_10 + IL_011c: ldloc.s V_11 + IL_011e: add + IL_011f: dup + IL_0120: stloc.s V_12 + IL_0122: stind.i4 + IL_0123: ldloc.s V_12 + IL_0125: add + IL_0126: stind.i4 + IL_0127: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_012c: ldsfld "System.Func Driver.<>c.<>9__0_3" + IL_0131: dup + IL_0132: brtrue.s IL_014b + IL_0134: pop + IL_0135: ldsfld "Driver.<>c Driver.<>c.<>9" + IL_013a: ldftn "int Driver.<>c.b__0_3()" + IL_0140: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0145: dup + IL_0146: stsfld "System.Func Driver.<>c.<>9__0_3" + IL_014b: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0150: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0155: stloc.s V_11 + IL_0157: ldloc.s V_11 + IL_0159: ldelema "S2" + IL_015e: ldflda "S1 S2.s1" + IL_0163: ldfld "int S1.x" + IL_0168: ret + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79415")] + public void CompoundAssignment() + { + var source = """ + using System; + using System.Threading.Tasks; + + interface I1 + { + int this[int i] { get; set; } + } + + struct S1 : I1 + { + public int F1; + + public int this[int i] + { + get + { + Console.Write(" g" + i); + return 0; + } + set + { + Console.Write(" s" + i + value); + } + } + } + + class Program + { + static async Task Main() + { + await Test3(); + } + + static T GetT() where T : I1 + { + Console.Write(" t"); + return (T)(object)new S1 { F1 = 123 }; + } + + static async Task Test3() where T : I1 + { + GetT()[Index()] += await Get1Async(); + } + + static async Task Get1Async() + { + Console.Write(" v"); + await Task.Yield(); + return 1; + } + + static int Index() + { + Console.Write(" i"); + return 2; + } + } + """; + var expectedOutput = "t i g2 v s21"; + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + + var comp = CreateRuntimeAsyncCompilation(source); + + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa } + [Test3]: Return value missing on the stack. { Offset = 0x7a } + [Get1Async]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 123 (0x7b) + .maxstack 4 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + int V_5, + T V_6) + IL_0000: call "T Program.GetT()" + IL_0005: stloc.0 + IL_0006: ldloca.s V_6 + IL_0008: initobj "T" + IL_000e: ldloc.s V_6 + IL_0010: box "T" + IL_0015: brtrue.s IL_0019 + IL_0017: ldloc.0 + IL_0018: stloc.1 + IL_0019: call "int Program.Index()" + IL_001e: stloc.2 + IL_001f: ldloc.2 + IL_0020: stloc.3 + IL_0021: ldloca.s V_6 + IL_0023: initobj "T" + IL_0029: ldloc.s V_6 + IL_002b: box "T" + IL_0030: brtrue.s IL_0036 + IL_0032: ldloca.s V_1 + IL_0034: br.s IL_0038 + IL_0036: ldloca.s V_0 + IL_0038: ldloc.2 + IL_0039: constrained. "T" + IL_003f: callvirt "int I1.this[int].get" + IL_0044: stloc.s V_4 + IL_0046: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_004b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0050: stloc.s V_5 + IL_0052: ldloca.s V_6 + IL_0054: initobj "T" + IL_005a: ldloc.s V_6 + IL_005c: box "T" + IL_0061: brtrue.s IL_0067 + IL_0063: ldloca.s V_1 + IL_0065: br.s IL_0069 + IL_0067: ldloca.s V_0 + IL_0069: ldloc.3 + IL_006a: ldloc.s V_4 + IL_006c: ldloc.s V_5 + IL_006e: add + IL_006f: constrained. "T" + IL_0075: callvirt "void I1.this[int].set" + IL_007a: ret + } + """); } [Fact] @@ -6314,72 +6512,70 @@ public static void Main() CompileAndVerify(source, expectedOutput: expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Run() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(11, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Run]: Return value missing on the stack. { Offset = 0x47 } - // [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 72 (0x48) - // .maxstack 2 - // .locals init (int V_0, //test - // int V_1, //count - // int V_2, - // TestCase V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // IL_0002: ldc.i4.0 - // IL_0003: stloc.1 - // .try - // { - // IL_0004: ldloc.0 - // IL_0005: ldc.i4.1 - // IL_0006: add - // IL_0007: stloc.0 - // IL_0008: ldloca.s V_3 - // IL_000a: initobj "TestCase" - // IL_0010: ldarg.0 - // IL_0011: call "System.Threading.Tasks.Task TestCase.Bar()" - // IL_0016: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_001b: stloc.2 - // IL_001c: ldloca.s V_3 - // IL_001e: ldloc.2 - // IL_001f: stfld "int TestCase.X" - // IL_0024: ldloc.3 - // IL_0025: ldfld "int TestCase.X" - // IL_002a: ldc.i4.1 - // IL_002b: bne.un.s IL_0031 - // IL_002d: ldloc.1 - // IL_002e: ldc.i4.1 - // IL_002f: add - // IL_0030: stloc.1 - // IL_0031: leave.s IL_0047 - // } - // finally - // { - // IL_0033: ldloc.0 - // IL_0034: ldloc.1 - // IL_0035: sub - // IL_0036: stsfld "int Driver.Result" - // IL_003b: ldsfld "System.Threading.AutoResetEvent Driver.CompleteSignal" - // IL_0040: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0045: pop - // IL_0046: endfinally - // } - // IL_0047: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Run]: Return value missing on the stack. { Offset = 0x50 } + [Bar]: Unexpected type on the stack. { Offset = 0xc, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 81 (0x51) + .maxstack 2 + .locals init (TestCase V_0, + int V_1, //test + int V_2, //count + int V_3, + TestCase V_4) + IL_0000: ldarg.0 + IL_0001: ldobj "TestCase" + IL_0006: stloc.0 + IL_0007: ldc.i4.0 + IL_0008: stloc.1 + IL_0009: ldc.i4.0 + IL_000a: stloc.2 + .try + { + IL_000b: ldloc.1 + IL_000c: ldc.i4.1 + IL_000d: add + IL_000e: stloc.1 + IL_000f: ldloca.s V_4 + IL_0011: initobj "TestCase" + IL_0017: ldloca.s V_0 + IL_0019: call "System.Threading.Tasks.Task TestCase.Bar()" + IL_001e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0023: stloc.3 + IL_0024: ldloca.s V_4 + IL_0026: ldloc.3 + IL_0027: stfld "int TestCase.X" + IL_002c: ldloc.s V_4 + IL_002e: ldfld "int TestCase.X" + IL_0033: ldc.i4.1 + IL_0034: bne.un.s IL_003a + IL_0036: ldloc.2 + IL_0037: ldc.i4.1 + IL_0038: add + IL_0039: stloc.2 + IL_003a: leave.s IL_0050 + } + finally + { + IL_003c: ldloc.1 + IL_003d: ldloc.2 + IL_003e: sub + IL_003f: stsfld "int Driver.Result" + IL_0044: ldsfld "System.Threading.AutoResetEvent Driver.CompleteSignal" + IL_0049: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_004e: pop + IL_004f: endfinally + } + IL_0050: ret + } + """); } [Fact] @@ -6511,10 +6707,12 @@ static void Main() Console.WriteLine(Driver.Result); } }"; - CompileAndVerify(source, "0"); + + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - var verifier = CompileAndVerify(comp, verify: Verification.Fails with + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } @@ -6700,91 +6898,93 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (21,15): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // x[await GetVal(0)] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(21, 15) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x6a } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 107 (0x6b) - // .maxstack 4 - // .locals init (int V_0, //tests - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldloc.0 - // IL_0003: ldc.i4.1 - // IL_0004: add - // IL_0005: stloc.0 - // IL_0006: ldc.i4.4 - // IL_0007: newarr "int" - // IL_000c: dup - // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" - // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - // IL_0017: dup - // IL_0018: ldarg.0 - // IL_0019: ldc.i4.0 - // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.1 - // IL_0025: ldloc.1 - // IL_0026: ldelema "int" - // IL_002b: dup - // IL_002c: ldind.i4 - // IL_002d: stloc.2 - // IL_002e: ldarg.0 - // IL_002f: ldc.i4.4 - // IL_0030: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0035: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003a: stloc.3 - // IL_003b: ldloc.2 - // IL_003c: ldloc.3 - // IL_003d: add - // IL_003e: stind.i4 - // IL_003f: ldc.i4.0 - // IL_0040: ldelem.i4 - // IL_0041: ldc.i4.5 - // IL_0042: bne.un.s IL_0050 - // IL_0044: ldsfld "int Driver.Count" - // IL_0049: ldc.i4.1 - // IL_004a: add - // IL_004b: stsfld "int Driver.Count" - // IL_0050: leave.s IL_006a - // } - // finally - // { - // IL_0052: ldsfld "int Driver.Count" - // IL_0057: ldloc.0 - // IL_0058: sub - // IL_0059: stsfld "int Driver.Result" - // IL_005e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0063: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0068: pop - // IL_0069: endfinally - // } - // IL_006a: ret - // } - // """); - } + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x73 } + """ + }); - [Fact] - public void SpillOperator_Compound2() - { + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 116 (0x74) + .maxstack 4 + .locals init (int V_0, + int V_1, //tests + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.1 + .try + { + IL_0002: ldloc.1 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.1 + IL_0006: ldc.i4.4 + IL_0007: newarr "int" + IL_000c: dup + IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0017: dup + IL_0018: ldarg.0 + IL_0019: ldc.i4.0 + IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.2 + IL_0025: ldloc.2 + IL_0026: stloc.0 + IL_0027: dup + IL_0028: ldloc.0 + IL_0029: ldelem.i4 + IL_002a: pop + IL_002b: dup + IL_002c: ldloc.0 + IL_002d: ldelem.i4 + IL_002e: stloc.3 + IL_002f: ldarg.0 + IL_0030: ldc.i4.4 + IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.s V_4 + IL_003d: ldloc.0 + IL_003e: ldelema "int" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: add + IL_0047: stind.i4 + IL_0048: ldc.i4.0 + IL_0049: ldelem.i4 + IL_004a: ldc.i4.5 + IL_004b: bne.un.s IL_0059 + IL_004d: ldsfld "int Driver.Count" + IL_0052: ldc.i4.1 + IL_0053: add + IL_0054: stsfld "int Driver.Count" + IL_0059: leave.s IL_0073 + } + finally + { + IL_005b: ldsfld "int Driver.Count" + IL_0060: ldloc.1 + IL_0061: sub + IL_0062: stsfld "int Driver.Result" + IL_0067: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_006c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0071: pop + IL_0072: endfinally + } + IL_0073: ret + } + """); + } + + [Fact] + public void SpillOperator_Compound2() + { var source = @" using System; using System.Threading; @@ -6896,86 +7096,88 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (21,15): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // x[await GetVal(0)] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(21, 15) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x6a } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 107 (0x6b) - // .maxstack 4 - // .locals init (int V_0, //tests - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldloc.0 - // IL_0003: ldc.i4.1 - // IL_0004: add - // IL_0005: stloc.0 - // IL_0006: ldc.i4.4 - // IL_0007: newarr "int" - // IL_000c: dup - // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" - // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - // IL_0017: dup - // IL_0018: ldarg.0 - // IL_0019: ldc.i4.0 - // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.1 - // IL_0025: ldloc.1 - // IL_0026: ldelema "int" - // IL_002b: dup - // IL_002c: ldind.i4 - // IL_002d: stloc.2 - // IL_002e: ldarg.0 - // IL_002f: ldc.i4.4 - // IL_0030: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0035: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003a: stloc.3 - // IL_003b: ldloc.2 - // IL_003c: ldloc.3 - // IL_003d: add - // IL_003e: stind.i4 - // IL_003f: ldc.i4.0 - // IL_0040: ldelem.i4 - // IL_0041: ldc.i4.5 - // IL_0042: bne.un.s IL_0050 - // IL_0044: ldsfld "int Driver.Count" - // IL_0049: ldc.i4.1 - // IL_004a: add - // IL_004b: stsfld "int Driver.Count" - // IL_0050: leave.s IL_006a - // } - // finally - // { - // IL_0052: ldsfld "int Driver.Count" - // IL_0057: ldloc.0 - // IL_0058: sub - // IL_0059: stsfld "int Driver.Result" - // IL_005e: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0063: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0068: pop - // IL_0069: endfinally - // } - // IL_006a: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x73 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 116 (0x74) + .maxstack 4 + .locals init (int V_0, + int V_1, //tests + int V_2, + int V_3, + int V_4) + IL_0000: ldc.i4.0 + IL_0001: stloc.1 + .try + { + IL_0002: ldloc.1 + IL_0003: ldc.i4.1 + IL_0004: add + IL_0005: stloc.1 + IL_0006: ldc.i4.4 + IL_0007: newarr "int" + IL_000c: dup + IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0017: dup + IL_0018: ldarg.0 + IL_0019: ldc.i4.0 + IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0024: stloc.2 + IL_0025: ldloc.2 + IL_0026: stloc.0 + IL_0027: dup + IL_0028: ldloc.0 + IL_0029: ldelem.i4 + IL_002a: pop + IL_002b: dup + IL_002c: ldloc.0 + IL_002d: ldelem.i4 + IL_002e: stloc.3 + IL_002f: ldarg.0 + IL_0030: ldc.i4.4 + IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_003b: stloc.s V_4 + IL_003d: ldloc.0 + IL_003e: ldelema "int" + IL_0043: ldloc.3 + IL_0044: ldloc.s V_4 + IL_0046: add + IL_0047: stind.i4 + IL_0048: ldc.i4.0 + IL_0049: ldelem.i4 + IL_004a: ldc.i4.5 + IL_004b: bne.un.s IL_0059 + IL_004d: ldsfld "int Driver.Count" + IL_0052: ldc.i4.1 + IL_0053: add + IL_0054: stsfld "int Driver.Count" + IL_0059: leave.s IL_0073 + } + finally + { + IL_005b: ldsfld "int Driver.Count" + IL_0060: ldloc.1 + IL_0061: sub + IL_0062: stsfld "int Driver.Result" + IL_0067: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_006c: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_0071: pop + IL_0072: endfinally + } + IL_0073: ret + } + """); } [Fact] @@ -7133,114 +7335,120 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (15,23): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Run() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Run").WithArguments("TestCase.Run()").WithLocation(15, 23), - // (23,21): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // val = x[await GetVal(0)] += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(0)").WithArguments("TestCase.Run()").WithLocation(23, 21) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x9d } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 158 (0x9e) - // .maxstack 5 - // .locals init (int V_0, //tests - // int V_1, - // int& V_2, - // int V_3, - // int V_4, - // int V_5, - // bool V_6) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // .try - // { - // IL_0002: ldloc.0 - // IL_0003: ldc.i4.1 - // IL_0004: add - // IL_0005: stloc.0 - // IL_0006: ldc.i4.4 - // IL_0007: newarr "int" - // IL_000c: dup - // IL_000d: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" - // IL_0012: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" - // IL_0017: dup - // IL_0018: ldarg.0 - // IL_0019: ldc.i4.0 - // IL_001a: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_001f: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0024: stloc.1 - // IL_0025: ldloc.1 - // IL_0026: ldelema "int" - // IL_002b: stloc.2 - // IL_002c: ldloc.2 - // IL_002d: ldind.i4 - // IL_002e: stloc.3 - // IL_002f: ldarg.0 - // IL_0030: ldc.i4.4 - // IL_0031: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0036: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_003b: stloc.s V_4 - // IL_003d: ldarg.0 - // IL_003e: ldloc.2 - // IL_003f: ldloc.3 - // IL_0040: ldloc.s V_4 - // IL_0042: add - // IL_0043: dup - // IL_0044: stloc.s V_5 - // IL_0046: stind.i4 - // IL_0047: ldloc.s V_5 - // IL_0049: stfld "int TestCase.val" - // IL_004e: ldc.i4.0 - // IL_004f: ldelem.i4 - // IL_0050: ldc.i4.5 - // IL_0051: ceq - // IL_0053: stloc.s V_6 - // IL_0055: ldloc.s V_6 - // IL_0057: brfalse.s IL_0073 - // IL_0059: ldarg.0 - // IL_005a: ldfld "int TestCase.val" - // IL_005f: ldarg.0 - // IL_0060: ldc.i4.5 - // IL_0061: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0066: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_006b: stloc.s V_4 - // IL_006d: ldloc.s V_4 - // IL_006f: ceq - // IL_0071: stloc.s V_6 - // IL_0073: ldloc.s V_6 - // IL_0075: brfalse.s IL_0083 - // IL_0077: ldsfld "int Driver.Count" - // IL_007c: ldc.i4.1 - // IL_007d: add - // IL_007e: stsfld "int Driver.Count" - // IL_0083: leave.s IL_009d - // } - // finally - // { - // IL_0085: ldsfld "int Driver.Count" - // IL_008a: ldloc.0 - // IL_008b: sub - // IL_008c: stsfld "int Driver.Result" - // IL_0091: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_0096: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_009b: pop - // IL_009c: endfinally - // } - // IL_009d: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0xb7 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 184 (0xb8) + .maxstack 5 + .locals init (TestCase V_0, + int[] V_1, + int V_2, + int V_3, //tests + int[] V_4, + int& V_5, + int V_6, + int V_7, + int V_8, + bool V_9) + IL_0000: ldarg.0 + IL_0001: ldobj "TestCase" + IL_0006: stloc.0 + IL_0007: ldc.i4.0 + IL_0008: stloc.3 + .try + { + IL_0009: ldloc.3 + IL_000a: ldc.i4.1 + IL_000b: add + IL_000c: stloc.3 + IL_000d: ldc.i4.4 + IL_000e: newarr "int" + IL_0013: dup + IL_0014: ldtoken ".__StaticArrayInitTypeSize=16 .CF97ADEEDB59E05BFD73A2B4C2A8885708C4F4F70C84C64B27120E72AB733B72" + IL_0019: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_001e: dup + IL_001f: stloc.s V_4 + IL_0021: ldloca.s V_0 + IL_0023: ldc.i4.0 + IL_0024: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0029: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002e: ldloc.s V_4 + IL_0030: stloc.1 + IL_0031: stloc.2 + IL_0032: ldloc.1 + IL_0033: ldloc.2 + IL_0034: ldelem.i4 + IL_0035: pop + IL_0036: ldloc.1 + IL_0037: ldloc.2 + IL_0038: ldelem.i4 + IL_0039: stloc.s V_6 + IL_003b: ldloca.s V_0 + IL_003d: ldc.i4.4 + IL_003e: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0043: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0048: stloc.s V_7 + IL_004a: ldloca.s V_0 + IL_004c: ldloc.1 + IL_004d: ldloc.2 + IL_004e: ldelema "int" + IL_0053: stloc.s V_5 + IL_0055: ldloc.s V_5 + IL_0057: ldloc.s V_6 + IL_0059: ldloc.s V_7 + IL_005b: add + IL_005c: dup + IL_005d: stloc.s V_8 + IL_005f: stind.i4 + IL_0060: ldloc.s V_8 + IL_0062: stfld "int TestCase.val" + IL_0067: ldc.i4.0 + IL_0068: ldelem.i4 + IL_0069: ldc.i4.5 + IL_006a: ceq + IL_006c: stloc.s V_9 + IL_006e: ldloc.s V_9 + IL_0070: brfalse.s IL_008d + IL_0072: ldloc.0 + IL_0073: ldfld "int TestCase.val" + IL_0078: ldloca.s V_0 + IL_007a: ldc.i4.5 + IL_007b: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0080: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0085: stloc.s V_7 + IL_0087: ldloc.s V_7 + IL_0089: ceq + IL_008b: stloc.s V_9 + IL_008d: ldloc.s V_9 + IL_008f: brfalse.s IL_009d + IL_0091: ldsfld "int Driver.Count" + IL_0096: ldc.i4.1 + IL_0097: add + IL_0098: stsfld "int Driver.Count" + IL_009d: leave.s IL_00b7 + } + finally + { + IL_009f: ldsfld "int Driver.Count" + IL_00a4: ldloc.3 + IL_00a5: sub + IL_00a6: stsfld "int Driver.Result" + IL_00ab: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_00b0: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_00b5: pop + IL_00b6: endfinally + } + IL_00b7: ret + } + """); } [Fact] @@ -7521,10 +7729,12 @@ static void Main() Console.WriteLine(t.Result); } }"; - CompileAndVerify(source, "42"); + + var expectedOutput = "42"; + CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - var verifier = CompileAndVerify(comp, verify: Verification.Fails with + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ [Run]: Unexpected type on the stack. { Offset = 0x47, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } @@ -7732,200 +7942,211 @@ static void Main() CompileAndVerify(source, "0"); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (39,42): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[0].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run()").WithLocation(39, 42), - // (39,42): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[0].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(4)").WithArguments("TestCase.Run()").WithLocation(39, 42), - // (44,30): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[await GetVal(1)].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(1)").WithArguments("TestCase.Run()").WithLocation(44, 30), - // (44,30): error CS9328: Method 'TestCase.Run()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // this.myClass.arr[await GetVal(1)].Field += await GetVal(4); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetVal(1)").WithArguments("TestCase.Run()").WithLocation(44, 30) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [Run]: Return value missing on the stack. { Offset = 0x182 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("TestCase.Run()", """ - // { - // // Code size 387 (0x183) - // .maxstack 3 - // .locals init (int V_0, //tests - // int V_1, - // int V_2, - // int V_3) - // IL_0000: ldc.i4.0 - // IL_0001: stloc.0 - // IL_0002: ldarg.0 - // IL_0003: newobj "TestCase.PrivClass..ctor()" - // IL_0008: stfld "TestCase.PrivClass TestCase.myClass" - // .try - // { - // IL_000d: ldloc.0 - // IL_000e: ldc.i4.1 - // IL_000f: add - // IL_0010: stloc.0 - // IL_0011: ldarg.0 - // IL_0012: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0017: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_001c: dup - // IL_001d: ldc.i4.0 - // IL_001e: ldelema "TestCase.PrivClass.ValueT" - // IL_0023: pop - // IL_0024: ldarg.0 - // IL_0025: ldc.i4.4 - // IL_0026: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_002b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0030: stloc.1 - // IL_0031: ldc.i4.0 - // IL_0032: ldelema "TestCase.PrivClass.ValueT" - // IL_0037: ldloc.1 - // IL_0038: stfld "int TestCase.PrivClass.ValueT.Field" - // IL_003d: ldarg.0 - // IL_003e: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0043: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_0048: ldc.i4.0 - // IL_0049: ldelema "TestCase.PrivClass.ValueT" - // IL_004e: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_0053: ldc.i4.4 - // IL_0054: bne.un.s IL_0062 - // IL_0056: ldsfld "int Driver.Count" - // IL_005b: ldc.i4.1 - // IL_005c: add - // IL_005d: stsfld "int Driver.Count" - // IL_0062: ldloc.0 - // IL_0063: ldc.i4.1 - // IL_0064: add - // IL_0065: stloc.0 - // IL_0066: ldarg.0 - // IL_0067: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_006c: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_0071: ldc.i4.0 - // IL_0072: ldelema "TestCase.PrivClass.ValueT" - // IL_0077: ldflda "int TestCase.PrivClass.ValueT.Field" - // IL_007c: dup - // IL_007d: ldind.i4 - // IL_007e: stloc.1 - // IL_007f: ldarg.0 - // IL_0080: ldc.i4.4 - // IL_0081: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_0086: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_008b: stloc.2 - // IL_008c: ldloc.1 - // IL_008d: ldloc.2 - // IL_008e: add - // IL_008f: stind.i4 - // IL_0090: ldarg.0 - // IL_0091: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0096: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_009b: ldc.i4.0 - // IL_009c: ldelema "TestCase.PrivClass.ValueT" - // IL_00a1: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_00a6: ldc.i4.8 - // IL_00a7: bne.un.s IL_00b5 - // IL_00a9: ldsfld "int Driver.Count" - // IL_00ae: ldc.i4.1 - // IL_00af: add - // IL_00b0: stsfld "int Driver.Count" - // IL_00b5: ldloc.0 - // IL_00b6: ldc.i4.1 - // IL_00b7: add - // IL_00b8: stloc.0 - // IL_00b9: ldarg.0 - // IL_00ba: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_00bf: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_00c4: ldarg.0 - // IL_00c5: ldc.i4.1 - // IL_00c6: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00cb: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00d0: stloc.2 - // IL_00d1: ldloc.2 - // IL_00d2: ldelema "TestCase.PrivClass.ValueT" - // IL_00d7: ldflda "int TestCase.PrivClass.ValueT.Field" - // IL_00dc: dup - // IL_00dd: ldind.i4 - // IL_00de: stloc.1 - // IL_00df: ldarg.0 - // IL_00e0: ldc.i4.4 - // IL_00e1: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_00e6: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_00eb: stloc.3 - // IL_00ec: ldloc.1 - // IL_00ed: ldloc.3 - // IL_00ee: add - // IL_00ef: stind.i4 - // IL_00f0: ldarg.0 - // IL_00f1: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_00f6: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_00fb: ldc.i4.1 - // IL_00fc: ldelema "TestCase.PrivClass.ValueT" - // IL_0101: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_0106: ldc.i4.4 - // IL_0107: bne.un.s IL_0115 - // IL_0109: ldsfld "int Driver.Count" - // IL_010e: ldc.i4.1 - // IL_010f: add - // IL_0110: stsfld "int Driver.Count" - // IL_0115: ldloc.0 - // IL_0116: ldc.i4.1 - // IL_0117: add - // IL_0118: stloc.0 - // IL_0119: ldarg.0 - // IL_011a: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_011f: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_0124: ldarg.0 - // IL_0125: ldc.i4.1 - // IL_0126: call "System.Threading.Tasks.Task TestCase.GetVal(int)" - // IL_012b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0130: stloc.3 - // IL_0131: ldloc.3 - // IL_0132: ldelema "TestCase.PrivClass.ValueT" - // IL_0137: ldflda "int TestCase.PrivClass.ValueT.Field" - // IL_013c: dup - // IL_013d: ldind.i4 - // IL_013e: stloc.1 - // IL_013f: ldloc.1 - // IL_0140: ldc.i4.1 - // IL_0141: add - // IL_0142: stind.i4 - // IL_0143: ldarg.0 - // IL_0144: ldfld "TestCase.PrivClass TestCase.myClass" - // IL_0149: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" - // IL_014e: ldc.i4.1 - // IL_014f: ldelema "TestCase.PrivClass.ValueT" - // IL_0154: ldfld "int TestCase.PrivClass.ValueT.Field" - // IL_0159: ldc.i4.5 - // IL_015a: bne.un.s IL_0168 - // IL_015c: ldsfld "int Driver.Count" - // IL_0161: ldc.i4.1 - // IL_0162: add - // IL_0163: stsfld "int Driver.Count" - // IL_0168: leave.s IL_0182 - // } - // finally - // { - // IL_016a: ldsfld "int Driver.Count" - // IL_016f: ldloc.0 - // IL_0170: sub - // IL_0171: stsfld "int Driver.Result" - // IL_0176: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" - // IL_017b: callvirt "bool System.Threading.EventWaitHandle.Set()" - // IL_0180: pop - // IL_0181: endfinally - // } - // IL_0182: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput("0", isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [GetVal]: Unexpected type on the stack. { Offset = 0xc, Found = value 'T', Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Run]: Return value missing on the stack. { Offset = 0x1c2 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("TestCase.Run()", """ + { + // Code size 451 (0x1c3) + .maxstack 3 + .locals init (int V_0, + int V_1, + int V_2, //tests + int V_3, + int V_4, + int V_5) + IL_0000: ldc.i4.0 + IL_0001: stloc.2 + IL_0002: ldarg.0 + IL_0003: newobj "TestCase.PrivClass..ctor()" + IL_0008: stfld "TestCase.PrivClass TestCase.myClass" + .try + { + IL_000d: ldloc.2 + IL_000e: ldc.i4.1 + IL_000f: add + IL_0010: stloc.2 + IL_0011: ldarg.0 + IL_0012: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0017: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_001c: dup + IL_001d: ldc.i4.0 + IL_001e: ldelema "TestCase.PrivClass.ValueT" + IL_0023: pop + IL_0024: ldarg.0 + IL_0025: ldc.i4.4 + IL_0026: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_002b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0030: stloc.3 + IL_0031: ldc.i4.0 + IL_0032: ldelema "TestCase.PrivClass.ValueT" + IL_0037: ldloc.3 + IL_0038: stfld "int TestCase.PrivClass.ValueT.Field" + IL_003d: ldarg.0 + IL_003e: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0043: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0048: ldc.i4.0 + IL_0049: ldelema "TestCase.PrivClass.ValueT" + IL_004e: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_0053: ldc.i4.4 + IL_0054: bne.un.s IL_0062 + IL_0056: ldsfld "int Driver.Count" + IL_005b: ldc.i4.1 + IL_005c: add + IL_005d: stsfld "int Driver.Count" + IL_0062: ldloc.2 + IL_0063: ldc.i4.1 + IL_0064: add + IL_0065: stloc.2 + IL_0066: ldarg.0 + IL_0067: ldfld "TestCase.PrivClass TestCase.myClass" + IL_006c: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0071: dup + IL_0072: ldc.i4.0 + IL_0073: ldelema "TestCase.PrivClass.ValueT" + IL_0078: pop + IL_0079: dup + IL_007a: ldc.i4.0 + IL_007b: ldelema "TestCase.PrivClass.ValueT" + IL_0080: pop + IL_0081: dup + IL_0082: ldc.i4.0 + IL_0083: ldelema "TestCase.PrivClass.ValueT" + IL_0088: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_008d: stloc.3 + IL_008e: ldarg.0 + IL_008f: ldc.i4.4 + IL_0090: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0095: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_009a: stloc.s V_4 + IL_009c: ldc.i4.0 + IL_009d: ldelema "TestCase.PrivClass.ValueT" + IL_00a2: ldflda "int TestCase.PrivClass.ValueT.Field" + IL_00a7: ldloc.3 + IL_00a8: ldloc.s V_4 + IL_00aa: add + IL_00ab: stind.i4 + IL_00ac: ldarg.0 + IL_00ad: ldfld "TestCase.PrivClass TestCase.myClass" + IL_00b2: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_00b7: ldc.i4.0 + IL_00b8: ldelema "TestCase.PrivClass.ValueT" + IL_00bd: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_00c2: ldc.i4.8 + IL_00c3: bne.un.s IL_00d1 + IL_00c5: ldsfld "int Driver.Count" + IL_00ca: ldc.i4.1 + IL_00cb: add + IL_00cc: stsfld "int Driver.Count" + IL_00d1: ldloc.2 + IL_00d2: ldc.i4.1 + IL_00d3: add + IL_00d4: stloc.2 + IL_00d5: ldarg.0 + IL_00d6: ldfld "TestCase.PrivClass TestCase.myClass" + IL_00db: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_00e0: ldarg.0 + IL_00e1: ldc.i4.1 + IL_00e2: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_00e7: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_00ec: stloc.s V_4 + IL_00ee: ldloc.s V_4 + IL_00f0: stloc.0 + IL_00f1: dup + IL_00f2: ldloc.0 + IL_00f3: ldelema "TestCase.PrivClass.ValueT" + IL_00f8: pop + IL_00f9: ldloc.0 + IL_00fa: stloc.1 + IL_00fb: dup + IL_00fc: ldloc.1 + IL_00fd: ldelema "TestCase.PrivClass.ValueT" + IL_0102: pop + IL_0103: dup + IL_0104: ldloc.1 + IL_0105: ldelema "TestCase.PrivClass.ValueT" + IL_010a: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_010f: stloc.3 + IL_0110: ldarg.0 + IL_0111: ldc.i4.4 + IL_0112: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0117: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_011c: stloc.s V_5 + IL_011e: ldloc.1 + IL_011f: ldelema "TestCase.PrivClass.ValueT" + IL_0124: ldflda "int TestCase.PrivClass.ValueT.Field" + IL_0129: ldloc.3 + IL_012a: ldloc.s V_5 + IL_012c: add + IL_012d: stind.i4 + IL_012e: ldarg.0 + IL_012f: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0134: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0139: ldc.i4.1 + IL_013a: ldelema "TestCase.PrivClass.ValueT" + IL_013f: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_0144: ldc.i4.4 + IL_0145: bne.un.s IL_0153 + IL_0147: ldsfld "int Driver.Count" + IL_014c: ldc.i4.1 + IL_014d: add + IL_014e: stsfld "int Driver.Count" + IL_0153: ldloc.2 + IL_0154: ldc.i4.1 + IL_0155: add + IL_0156: stloc.2 + IL_0157: ldarg.0 + IL_0158: ldfld "TestCase.PrivClass TestCase.myClass" + IL_015d: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_0162: ldarg.0 + IL_0163: ldc.i4.1 + IL_0164: call "System.Threading.Tasks.Task TestCase.GetVal(int)" + IL_0169: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_016e: stloc.s V_5 + IL_0170: ldloc.s V_5 + IL_0172: ldelema "TestCase.PrivClass.ValueT" + IL_0177: ldflda "int TestCase.PrivClass.ValueT.Field" + IL_017c: dup + IL_017d: ldind.i4 + IL_017e: stloc.3 + IL_017f: ldloc.3 + IL_0180: ldc.i4.1 + IL_0181: add + IL_0182: stind.i4 + IL_0183: ldarg.0 + IL_0184: ldfld "TestCase.PrivClass TestCase.myClass" + IL_0189: ldfld "TestCase.PrivClass.ValueT[] TestCase.PrivClass.arr" + IL_018e: ldc.i4.1 + IL_018f: ldelema "TestCase.PrivClass.ValueT" + IL_0194: ldfld "int TestCase.PrivClass.ValueT.Field" + IL_0199: ldc.i4.5 + IL_019a: bne.un.s IL_01a8 + IL_019c: ldsfld "int Driver.Count" + IL_01a1: ldc.i4.1 + IL_01a2: add + IL_01a3: stsfld "int Driver.Count" + IL_01a8: leave.s IL_01c2 + } + finally + { + IL_01aa: ldsfld "int Driver.Count" + IL_01af: ldloc.2 + IL_01b0: sub + IL_01b1: stsfld "int Driver.Result" + IL_01b6: ldsfld "System.Threading.AutoResetEvent Driver.CompletedSignal" + IL_01bb: callvirt "bool System.Threading.EventWaitHandle.Set()" + IL_01c0: pop + IL_01c1: endfinally + } + IL_01c2: ret + } + """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -7951,63 +8172,60 @@ static async Task Main() CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,34): error CS9328: Method 'S.Main()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // Console.WriteLine(s.i += await GetInt()); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetInt()").WithArguments("S.Main()").WithLocation(11, 34) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Main]: Return value missing on the stack. { Offset = 0x63 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("S.Main()", """ - // { - // // Code size 100 (0x64) - // .maxstack 3 - // .locals init (S V_0, //s - // int? V_1, - // int? V_2, - // int? V_3) - // IL_0000: ldloca.s V_0 - // IL_0002: initobj "S" - // IL_0008: ldloca.s V_0 - // IL_000a: ldflda "int? S.i" - // IL_000f: dup - // IL_0010: ldobj "int?" - // IL_0015: stloc.1 - // IL_0016: call "System.Threading.Tasks.Task S.GetInt()" - // IL_001b: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0020: stloc.2 - // IL_0021: ldloca.s V_1 - // IL_0023: call "readonly bool int?.HasValue.get" - // IL_0028: ldloca.s V_2 - // IL_002a: call "readonly bool int?.HasValue.get" - // IL_002f: and - // IL_0030: brtrue.s IL_003d - // IL_0032: ldloca.s V_3 - // IL_0034: initobj "int?" - // IL_003a: ldloc.3 - // IL_003b: br.s IL_0051 - // IL_003d: ldloca.s V_1 - // IL_003f: call "readonly int int?.GetValueOrDefault()" - // IL_0044: ldloca.s V_2 - // IL_0046: call "readonly int int?.GetValueOrDefault()" - // IL_004b: add - // IL_004c: newobj "int?..ctor(int)" - // IL_0051: dup - // IL_0052: stloc.3 - // IL_0053: stobj "int?" - // IL_0058: ldloc.3 - // IL_0059: box "int?" - // IL_005e: call "void System.Console.WriteLine(object)" - // IL_0063: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x68 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.Main()", """ + { + // Code size 105 (0x69) + .maxstack 3 + .locals init (S V_0, + S V_1, + int? V_2, + int? V_3, + int? V_4) + IL_0000: ldloca.s V_1 + IL_0002: initobj "S" + IL_0008: ldloc.1 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldfld "int? S.i" + IL_0010: stloc.2 + IL_0011: call "System.Threading.Tasks.Task S.GetInt()" + IL_0016: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.3 + IL_001c: ldloca.s V_0 + IL_001e: ldflda "int? S.i" + IL_0023: ldloca.s V_2 + IL_0025: call "readonly bool int?.HasValue.get" + IL_002a: ldloca.s V_3 + IL_002c: call "readonly bool int?.HasValue.get" + IL_0031: and + IL_0032: brtrue.s IL_0040 + IL_0034: ldloca.s V_4 + IL_0036: initobj "int?" + IL_003c: ldloc.s V_4 + IL_003e: br.s IL_0054 + IL_0040: ldloca.s V_2 + IL_0042: call "readonly int int?.GetValueOrDefault()" + IL_0047: ldloca.s V_3 + IL_0049: call "readonly int int?.GetValueOrDefault()" + IL_004e: add + IL_004f: newobj "int?..ctor(int)" + IL_0054: dup + IL_0055: stloc.s V_4 + IL_0057: stobj "int?" + IL_005c: ldloc.s V_4 + IL_005e: box "int?" + IL_0063: call "void System.Console.WriteLine(object)" + IL_0068: ret + } + """); } [Fact, WorkItem(36443, "https://github.com/dotnet/roslyn/issues/36443")] @@ -8185,199 +8403,1405 @@ static async Task Main() CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (11,34): error CS9328: Method 'S.M(S)' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // Console.WriteLine(s.i += await GetInt()); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await GetInt()").WithArguments("S.M(S)").WithLocation(11, 34), + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x65 } + [Main]: Return value missing on the stack. { Offset = 0xf } + """ + }); + + verifier.VerifyDiagnostics( // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // M(); - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9), - // (14,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 Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23) + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9) ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [M]: Return value missing on the stack. { Offset = 0x63 } - // [Main]: Return value missing on the stack. { Offset = 0xf } - // """ - // }); - - // verifier.VerifyDiagnostics( - // // (14,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 Task Main() - // Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(14, 23), - // // (16,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. - // // M(); - // Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "M()").WithLocation(16, 9) - // ); - // verifier.VerifyIL("S.M(S)", """ - // { - // // Code size 100 (0x64) - // .maxstack 3 - // .locals init (int? V_0, - // int? V_1, - // int? V_2) - // IL_0000: ldarga.s V_0 - // IL_0002: initobj "S" - // IL_0008: ldarga.s V_0 - // IL_000a: ldflda "int? S.i" - // IL_000f: dup - // IL_0010: ldobj "int?" - // IL_0015: stloc.0 - // IL_0016: call "System.Threading.Tasks.Task S.GetInt()" - // IL_001b: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0020: stloc.1 - // IL_0021: ldloca.s V_0 - // IL_0023: call "readonly bool int?.HasValue.get" - // IL_0028: ldloca.s V_1 - // IL_002a: call "readonly bool int?.HasValue.get" - // IL_002f: and - // IL_0030: brtrue.s IL_003d - // IL_0032: ldloca.s V_2 - // IL_0034: initobj "int?" - // IL_003a: ldloc.2 - // IL_003b: br.s IL_0051 - // IL_003d: ldloca.s V_0 - // IL_003f: call "readonly int int?.GetValueOrDefault()" - // IL_0044: ldloca.s V_1 - // IL_0046: call "readonly int int?.GetValueOrDefault()" - // IL_004b: add - // IL_004c: newobj "int?..ctor(int)" - // IL_0051: dup - // IL_0052: stloc.2 - // IL_0053: stobj "int?" - // IL_0058: ldloc.2 - // IL_0059: box "int?" - // IL_005e: call "void System.Console.WriteLine(object)" - // IL_0063: ret - // } - // """); + verifier.VerifyIL("S.M(S)", """ + { + // Code size 102 (0x66) + .maxstack 3 + .locals init (S V_0, + int? V_1, + int? V_2, + int? V_3) + IL_0000: ldarga.s V_0 + IL_0002: initobj "S" + IL_0008: ldarg.0 + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldfld "int? S.i" + IL_0010: stloc.1 + IL_0011: call "System.Threading.Tasks.Task S.GetInt()" + IL_0016: call "int? System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_001b: stloc.2 + IL_001c: ldloca.s V_0 + IL_001e: ldflda "int? S.i" + IL_0023: ldloca.s V_1 + IL_0025: call "readonly bool int?.HasValue.get" + IL_002a: ldloca.s V_2 + IL_002c: call "readonly bool int?.HasValue.get" + IL_0031: and + IL_0032: brtrue.s IL_003f + IL_0034: ldloca.s V_3 + IL_0036: initobj "int?" + IL_003c: ldloc.3 + IL_003d: br.s IL_0053 + IL_003f: ldloca.s V_1 + IL_0041: call "readonly int int?.GetValueOrDefault()" + IL_0046: ldloca.s V_2 + IL_0048: call "readonly int int?.GetValueOrDefault()" + IL_004d: add + IL_004e: newobj "int?..ctor(int)" + IL_0053: dup + IL_0054: stloc.3 + IL_0055: stobj "int?" + IL_005a: ldloc.3 + IL_005b: box "int?" + IL_0060: call "void System.Console.WriteLine(object)" + IL_0065: ret + } + """); } [Fact] - public void SpillSacrificialRead() + public void SpillAssignmentToThisStruct_01() { - var source = @" -using System; -using System.Threading.Tasks; - -class C -{ - static void F1(ref int x, int y, int z) - { - x += y + z; - } + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; - static int F0() - { - Console.WriteLine(-1); - return 0; - } + async Task M() + { + i = 1; // Not observable outside of the method + return; + } - static async Task F2() - { - int[] x = new int[1] { 21 }; - x = null; - F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); - return x[0]; - } + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } - public static void Main() - { - var t = F2(); - try - { - t.Wait(); - } - catch(Exception) - { - Console.WriteLine(0); - return; - } + static Task GetInt() => Task.FromResult(1); + } + """; - Console.WriteLine(-1); - } -}"; - CompileAndVerify(source, "0"); + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (22,28): error CS9328: Method 'C.F2()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "await Task.Factory.StartNew(() => 21)").WithArguments("C.F2()").WithLocation(22, 28) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [F2]: Unexpected type on the stack. { Offset = 0x52, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("C.F2()", """ - // { - // // Code size 83 (0x53) - // .maxstack 5 - // .locals init (int V_0, - // int V_1) - // IL_0000: ldc.i4.1 - // IL_0001: newarr "int" - // IL_0006: dup - // IL_0007: ldc.i4.0 - // IL_0008: ldc.i4.s 21 - // IL_000a: stelem.i4 - // IL_000b: pop - // IL_000c: ldnull - // IL_000d: dup - // IL_000e: ldc.i4.0 - // IL_000f: ldelema "int" - // IL_0014: call "int C.F0()" - // IL_0019: stloc.0 - // IL_001a: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" - // IL_001f: ldsfld "System.Func C.<>c.<>9__2_0" - // IL_0024: dup - // IL_0025: brtrue.s IL_003e - // IL_0027: pop - // IL_0028: ldsfld "C.<>c C.<>c.<>9" - // IL_002d: ldftn "int C.<>c.b__2_0()" - // IL_0033: newobj "System.Func..ctor(object, System.IntPtr)" - // IL_0038: dup - // IL_0039: stsfld "System.Func C.<>c.<>9__2_0" - // IL_003e: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" - // IL_0043: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0048: stloc.1 - // IL_0049: ldloc.0 - // IL_004a: ldloc.1 - // IL_004b: call "void C.F1(ref int, int, int)" - // IL_0050: ldc.i4.0 - // IL_0051: ldelem.i4 - // IL_0052: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0xf } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 16 (0x10) + .maxstack 2 + .locals init (S V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: ldc.i4.1 + IL_000a: stfld "int S.i" + IL_000f: ret + } + """); } [Fact] - public void SpillRefThisStruct() + public void SpillAssignmentToThisStruct_02() { - var source = @" -using System; -using System.Threading.Tasks; + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int P { get; set; } -struct s1 -{ - public int X; + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.P); + } - public async void Goo1() - { - Bar(ref this, await Task.FromResult(42)); - } + static Task GetInt() => Task.FromResult(1); + } - public void Goo2() - { + interface I + { + public int P { get; set; } + } + + static class Extensions + { + extension(T t) where T : I + { + public async Task M() + { + t.P = 1; + return; + } + } + } + """; + + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x1f } + [M]: Return value missing on the stack. { Offset = 0xe } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Extensions.M(this T)", """ + { + // Code size 15 (0xf) + .maxstack 2 + IL_0000: ldarga.s V_0 + IL_0002: ldc.i4.1 + IL_0003: constrained. "T" + IL_0009: callvirt "void I.P.set" + IL_000e: ret + } + """); + } + + [Fact] + public void SpillAssignmentToThisStruct_03() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int P { get; set; } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.P); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int P { get; set; } + } + + static class Extensions + { + extension(ref T t) where T : struct, I + { + public async Task M() + { + t.P = 1; + return; + } + } + } + """; + + var expectedDiagnostics = new[] { + // (26,27): error CS1988: Async methods cannot have ref, in or out parameters + // public async Task M() + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "M").WithLocation(26, 27) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void SpillAssignmentToThisStruct_04() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int P { get; set; } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.P); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int P { get; set; } + } + + static class Extensions + { + extension(ref T t) where T : I + { + public async Task M() + { + t.P = 1; + return; + } + } + } + """; + + var expectedDiagnostics = new[] { + // (24,22): error CS9300: The 'ref' receiver parameter of an extension block must be a value type or a generic type constrained to struct. + // extension(ref T t) where T : I + Diagnostic(ErrorCode.ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne, "T").WithLocation(24, 22), + // (26,27): error CS1988: Async methods cannot have ref, in or out parameters + // public async Task M() + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "M").WithLocation(26, 27) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular14); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void SpillAssignmentToThisStruct_05() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + readonly async Task M() + { + await Task.Yield(); + Console.Write(i); + return; + } + + static async Task Main() + { + S s = default; + await s.M(); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x34 } + [Main]: Return value missing on the stack. { Offset = 0x14 } + """ + }); + + verifier.VerifyDiagnostics( + // (5,9): warning CS0649: Field 'S.i' is never assigned to, and will always have its default value 0 + // int i; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "i").WithArguments("S.i", "0").WithLocation(5, 9) + ); + verifier.VerifyIL("S.M()", """ + { + // Code size 53 (0x35) + .maxstack 2 + .locals init (System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_0, + System.Runtime.CompilerServices.YieldAwaitable V_1) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_000b: stloc.1 + IL_000c: ldloca.s V_1 + IL_000e: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0013: stloc.0 + IL_0014: ldloca.s V_0 + IL_0016: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_001b: brtrue.s IL_0023 + IL_001d: ldloc.0 + IL_001e: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0023: ldloca.s V_0 + IL_0025: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_002a: ldfld "int S.i" + IL_002f: call "void System.Console.Write(int)" + IL_0034: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_01() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + async Task M() + { + Console.Write(i += await GetInt()); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x2b } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (S V_0, + int V_1, + int V_2, + int V_3) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldfld "int S.i" + IL_000d: stloc.1 + IL_000e: call "System.Threading.Tasks.Task S.GetInt()" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloca.s V_0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: add + IL_001e: dup + IL_001f: stloc.3 + IL_0020: stfld "int S.i" + IL_0025: ldloc.3 + IL_0026: call "void System.Console.Write(int)" + IL_002b: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_02() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public static S operator +(S s, int value) + { + s.i += value; + return s; + } + + async Task M() + { + Console.Write((this += await GetInt()).i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x21 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Threading.Tasks.Task S.GetInt()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "S S.op_Addition(S, int)" + IL_0017: ldfld "int S.i" + IL_001c: call "void System.Console.Write(int)" + IL_0021: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_03() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public static S operator +(S s, int value) + { + s.i += value; + return s; + } + + async Task M() + { + this += await GetInt(); + Console.Write(i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x21 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 34 (0x22) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Threading.Tasks.Task S.GetInt()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "S S.op_Addition(S, int)" + IL_0017: ldfld "int S.i" + IL_001c: call "void System.Console.Write(int)" + IL_0021: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_04() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public static S operator +(S s, int value) + { + s.i += value; + return s; + } + + async Task M() + { + this += await GetInt(); + this += await GetInt(); + Console.Write(i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "20"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x32 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 51 (0x33) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: call "System.Threading.Tasks.Task S.GetInt()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.0 + IL_0011: ldloc.0 + IL_0012: call "S S.op_Addition(S, int)" + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.0 + IL_0022: ldloc.0 + IL_0023: call "S S.op_Addition(S, int)" + IL_0028: ldfld "int S.i" + IL_002d: call "void System.Console.Write(int)" + IL_0032: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToThisStruct_05() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S + { + int i; + + public void operator +=(int value) + { + this.i += value; + } + + async Task M() + { + this += await GetInt(); + Console.Write(i); + } + + static async Task Main() + { + S s = default; + await s.M(); + Console.Write(s.i); + } + + static Task GetInt() => Task.FromResult(1); + } + """; + + var expectedOutput = "10"; + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x25 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M()", """ + { + // Code size 38 (0x26) + .maxstack 2 + .locals init (S V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: ldobj "S" + IL_0006: stloc.0 + IL_0007: call "System.Threading.Tasks.Task S.GetInt()" + IL_000c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0011: stloc.1 + IL_0012: ldloca.s V_0 + IL_0014: ldloc.1 + IL_0015: call "void S.op_AdditionAssignment(int)" + IL_001a: ldloc.0 + IL_001b: ldfld "int S.i" + IL_0020: call "void System.Console.Write(int)" + IL_0025: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_01() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public int Prop { get; set; } + + static async Task M(T t) where T : I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = default; + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int Prop { get; set; } + } + """; + + var expectedOutput = "10"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x72 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 115 (0x73) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_5 + IL_0004: initobj "T" + IL_000a: ldloc.s V_5 + IL_000c: box "T" + IL_0011: brtrue.s IL_0015 + IL_0013: ldloc.0 + IL_0014: stloc.1 + IL_0015: ldloca.s V_5 + IL_0017: initobj "T" + IL_001d: ldloc.s V_5 + IL_001f: box "T" + IL_0024: brtrue.s IL_002a + IL_0026: ldloca.s V_1 + IL_0028: br.s IL_002c + IL_002a: ldloca.s V_0 + IL_002c: constrained. "T" + IL_0032: callvirt "int I.Prop.get" + IL_0037: stloc.2 + IL_0038: call "System.Threading.Tasks.Task S.GetInt()" + IL_003d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0042: stloc.3 + IL_0043: ldloca.s V_5 + IL_0045: initobj "T" + IL_004b: ldloc.s V_5 + IL_004d: box "T" + IL_0052: brtrue.s IL_0058 + IL_0054: ldloca.s V_1 + IL_0056: br.s IL_005a + IL_0058: ldloca.s V_0 + IL_005a: ldloc.2 + IL_005b: ldloc.3 + IL_005c: add + IL_005d: dup + IL_005e: stloc.s V_4 + IL_0060: constrained. "T" + IL_0066: callvirt "void I.Prop.set" + IL_006b: ldloc.s V_4 + IL_006d: call "void System.Console.Write(int)" + IL_0072: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_02() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public static S operator +(S left, int value) + { + left.Prop += value; + return left; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = default; + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public static abstract T operator +(T left, int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "10" : null; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x72 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 115 (0x73) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_5 + IL_0004: initobj "T" + IL_000a: ldloc.s V_5 + IL_000c: box "T" + IL_0011: brtrue.s IL_0015 + IL_0013: ldloc.0 + IL_0014: stloc.1 + IL_0015: ldloca.s V_5 + IL_0017: initobj "T" + IL_001d: ldloc.s V_5 + IL_001f: box "T" + IL_0024: brtrue.s IL_002a + IL_0026: ldloca.s V_1 + IL_0028: br.s IL_002c + IL_002a: ldloca.s V_0 + IL_002c: constrained. "T" + IL_0032: callvirt "int I.Prop.get" + IL_0037: stloc.2 + IL_0038: call "System.Threading.Tasks.Task S.GetInt()" + IL_003d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0042: stloc.3 + IL_0043: ldloca.s V_5 + IL_0045: initobj "T" + IL_004b: ldloc.s V_5 + IL_004d: box "T" + IL_0052: brtrue.s IL_0058 + IL_0054: ldloca.s V_1 + IL_0056: br.s IL_005a + IL_0058: ldloca.s V_0 + IL_005a: ldloc.2 + IL_005b: ldloc.3 + IL_005c: add + IL_005d: dup + IL_005e: stloc.s V_4 + IL_0060: constrained. "T" + IL_0066: callvirt "void I.Prop.set" + IL_006b: ldloc.s V_4 + IL_006d: call "void System.Console.Write(int)" + IL_0072: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_03() + { + var source = """ + using System; + using System.Threading.Tasks; + struct S : I + { + public void operator +=(int value) + { + this.Prop += value; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = default; + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public abstract void operator +=(int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = "10"; + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x72 } + [Main]: Return value missing on the stack. { Offset = 0x1f } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 115 (0x73) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_5 + IL_0004: initobj "T" + IL_000a: ldloc.s V_5 + IL_000c: box "T" + IL_0011: brtrue.s IL_0015 + IL_0013: ldloc.0 + IL_0014: stloc.1 + IL_0015: ldloca.s V_5 + IL_0017: initobj "T" + IL_001d: ldloc.s V_5 + IL_001f: box "T" + IL_0024: brtrue.s IL_002a + IL_0026: ldloca.s V_1 + IL_0028: br.s IL_002c + IL_002a: ldloca.s V_0 + IL_002c: constrained. "T" + IL_0032: callvirt "int I.Prop.get" + IL_0037: stloc.2 + IL_0038: call "System.Threading.Tasks.Task S.GetInt()" + IL_003d: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0042: stloc.3 + IL_0043: ldloca.s V_5 + IL_0045: initobj "T" + IL_004b: ldloc.s V_5 + IL_004d: box "T" + IL_0052: brtrue.s IL_0058 + IL_0054: ldloca.s V_1 + IL_0056: br.s IL_005a + IL_0058: ldloca.s V_0 + IL_005a: ldloc.2 + IL_005b: ldloc.3 + IL_005c: add + IL_005d: dup + IL_005e: stloc.s V_4 + IL_0060: constrained. "T" + IL_0066: callvirt "void I.Prop.set" + IL_006b: ldloc.s V_4 + IL_006d: call "void System.Console.Write(int)" + IL_0072: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_04() + { + var source = """ + using System; + using System.Threading.Tasks; + class S : I + { + public int Prop { get; set; } + + static async Task M(T t) where T : class, I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = new(); + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I + { + public int Prop { get; set; } + } + """; + + var expectedOutput = "11"; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x3a } + [Main]: Return value missing on the stack. { Offset = 0x1a } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: dup + IL_0005: ldobj "T" + IL_000a: stloc.1 + IL_000b: constrained. "T" + IL_0011: callvirt "int I.Prop.get" + IL_0016: stloc.2 + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.3 + IL_0022: ldloc.1 + IL_0023: box "T" + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: add + IL_002b: dup + IL_002c: stloc.s V_4 + IL_002e: callvirt "void I.Prop.set" + IL_0033: ldloc.s V_4 + IL_0035: call "void System.Console.Write(int)" + IL_003a: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_05() + { + var source = """ + using System; + using System.Threading.Tasks; + class S : I + { + public static S operator +(S left, int value) + { + left.Prop += value; + return left; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : class, I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = new(); + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public static abstract T operator +(T left, int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = ExecutionConditionUtil.IsCoreClr ? "11" : null; + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + CompileAndVerify(source, expectedOutput: expectedOutput, options: TestOptions.DebugExe, targetFramework: TargetFramework.Net90, verify: Verification.FailsPEVerify); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x3a } + [Main]: Return value missing on the stack. { Offset = 0x1a } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: dup + IL_0005: ldobj "T" + IL_000a: stloc.1 + IL_000b: constrained. "T" + IL_0011: callvirt "int I.Prop.get" + IL_0016: stloc.2 + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.3 + IL_0022: ldloc.1 + IL_0023: box "T" + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: add + IL_002b: dup + IL_002c: stloc.s V_4 + IL_002e: callvirt "void I.Prop.set" + IL_0033: ldloc.s V_4 + IL_0035: call "void System.Console.Write(int)" + IL_003a: ret + } + """); + } + + [Fact] + public void SpillCompoundAssignmentToTypeParameter_06() + { + var source = """ + using System; + using System.Threading.Tasks; + class S : I + { + public void operator +=(int value) + { + this.Prop += value; + } + + public int Prop { get; set; } + + static async Task M(T t) where T : class, I + { + Console.Write(t.Prop += await GetInt()); + } + + static async Task Main() + { + S s = new(); + await M(s); + Console.Write(s.Prop); + } + + static Task GetInt() => Task.FromResult(1); + } + + interface I where T : I + { + public abstract void operator +=(int value); + public int Prop { get; set; } + } + """; + + var expectedOutput = "11"; + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.ReleaseExe); + CompileAndVerify([source, CompilerFeatureRequiredAttribute], expectedOutput: expectedOutput, options: TestOptions.DebugExe); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [M]: Return value missing on the stack. { Offset = 0x3a } + [Main]: Return value missing on the stack. { Offset = 0x1a } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("S.M(T)", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (T V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: dup + IL_0005: ldobj "T" + IL_000a: stloc.1 + IL_000b: constrained. "T" + IL_0011: callvirt "int I.Prop.get" + IL_0016: stloc.2 + IL_0017: call "System.Threading.Tasks.Task S.GetInt()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.3 + IL_0022: ldloc.1 + IL_0023: box "T" + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: add + IL_002b: dup + IL_002c: stloc.s V_4 + IL_002e: callvirt "void I.Prop.set" + IL_0033: ldloc.s V_4 + IL_0035: call "void System.Console.Write(int)" + IL_003a: ret + } + """); + } + + [Fact] + public void SpillSacrificialRead() + { + var source = @" +using System; +using System.Threading.Tasks; + +class C +{ + static void F1(ref int x, int y, int z) + { + x += y + z; + } + + static int F0() + { + Console.WriteLine(-1); + return 0; + } + + static async Task F2() + { + int[] x = new int[1] { 21 }; + ClearX(); + F1(ref x[0], F0(), await Task.Factory.StartNew(() => 21)); + return x[0]; + + void ClearX() => x = null; + } + + public static void Main() + { + var t = F2(); + try + { + t.Wait(); + } + catch(Exception) + { + Console.WriteLine(0); + return; + } + + Console.WriteLine(-1); + } +}"; + var expectedOutput = "0"; + CompileAndVerify(source, expectedOutput); + + var comp = CreateRuntimeAsyncCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [F2]: Unexpected type on the stack. { Offset = 0x6d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.F2()", """ + { + // Code size 110 (0x6e) + .maxstack 5 + .locals init (C.<>c__DisplayClass2_0 V_0, //CS$<>8__locals0 + int V_1, + int V_2) + 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: ldc.i4.s 21 + IL_000c: stelem.i4 + IL_000d: stfld "int[] C.<>c__DisplayClass2_0.x" + IL_0012: ldloca.s V_0 + IL_0014: call "void C.g__ClearX|2_0(ref C.<>c__DisplayClass2_0)" + IL_0019: ldloc.0 + IL_001a: ldfld "int[] C.<>c__DisplayClass2_0.x" + IL_001f: dup + IL_0020: ldc.i4.0 + IL_0021: ldelem.i4 + IL_0022: pop + IL_0023: call "int C.F0()" + IL_0028: stloc.1 + IL_0029: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_002e: ldsfld "System.Func C.<>c.<>9__2_1" + IL_0033: dup + IL_0034: brtrue.s IL_004d + IL_0036: pop + IL_0037: ldsfld "C.<>c C.<>c.<>9" + IL_003c: ldftn "int C.<>c.b__2_1()" + IL_0042: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0047: dup + IL_0048: stsfld "System.Func C.<>c.<>9__2_1" + IL_004d: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0052: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.2 + IL_0058: ldc.i4.0 + IL_0059: ldelema "int" + IL_005e: ldloc.1 + IL_005f: ldloc.2 + IL_0060: call "void C.F1(ref int, int, int)" + IL_0065: ldloc.0 + IL_0066: ldfld "int[] C.<>c__DisplayClass2_0.x" + IL_006b: ldc.i4.0 + IL_006c: ldelem.i4 + IL_006d: ret + } + """); + } + + [Fact] + public void SpillRefThisStruct() + { + var source = @" +using System; +using System.Threading.Tasks; + +struct s1 +{ + public int X; + + public async void Goo1() + { + Bar(ref this, await Task.FromResult(42)); + } + + public void Goo2() + { Bar(ref this, 42); } @@ -8542,54 +9966,52 @@ public static void Main() CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - comp.VerifyEmitDiagnostics( - // (9,23): error CS9328: Method 's1.Goo1()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task Goo1() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "Goo1").WithArguments("s1.Goo1()").WithLocation(9, 23) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Goo1]: Return value missing on the stack. { Offset = 0x15 } - // [Goo1]: Return value missing on the stack. { Offset = 0x15 } - // """ - // }); - - // verifier.VerifyDiagnostics(); - // verifier.VerifyIL("s1.Goo1()", """ - // { - // // Code size 22 (0x16) - // .maxstack 3 - // .locals init (int V_0) - // IL_0000: ldc.i4.s 42 - // IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" - // IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000c: stloc.0 - // IL_000d: ldarg.0 - // IL_000e: ldarg.0 - // IL_000f: ldloc.0 - // IL_0010: call "void s1.Bar(ref s1, int)" - // IL_0015: ret - // } - // """); - - // verifier.VerifyIL("c1.Goo1()", """ - // { - // // Code size 22 (0x16) - // .maxstack 3 - // .locals init (int V_0) - // IL_0000: ldc.i4.s 42 - // IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" - // IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_000c: stloc.0 - // IL_000d: ldarg.0 - // IL_000e: ldarg.0 - // IL_000f: ldloc.0 - // IL_0010: call "void c1.Bar(c1, int)" - // IL_0015: ret - // } - // """); + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Goo1]: Return value missing on the stack. { Offset = 0x1e } + [Goo1]: Return value missing on the stack. { Offset = 0x15 } + """ + }); + + verifier.VerifyDiagnostics(); + verifier.VerifyIL("s1.Goo1()", """ + { + // Code size 31 (0x1f) + .maxstack 3 + .locals init (s1 V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: ldobj "s1" + IL_0006: stloc.0 + IL_0007: ldc.i4.s 42 + IL_0009: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_000e: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0013: stloc.1 + IL_0014: ldloca.s V_0 + IL_0016: ldloca.s V_0 + IL_0018: ldloc.1 + IL_0019: call "void s1.Bar(ref s1, int)" + IL_001e: ret + } + """); + + verifier.VerifyIL("c1.Goo1()", """ + { + // Code size 22 (0x16) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldc.i4.s 42 + IL_0002: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.FromResult(int)" + IL_0007: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000c: stloc.0 + IL_000d: ldarg.0 + IL_000e: ldarg.0 + IL_000f: ldloc.0 + IL_0010: call "void c1.Bar(c1, int)" + IL_0015: ret + } + """); } [Fact] @@ -8832,10 +10254,11 @@ public static void Main() } "; - var v = CompileAndVerify(source, "42"); + var expectedOutput = "42"; + var v = CompileAndVerify(source, expectedOutput); var comp = CreateRuntimeAsyncCompilation(source); - var verifier = CompileAndVerify(comp, verify: Verification.Fails with + var verifier = CompileAndVerify(comp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with { ILVerifyMessage = """ [M]: Return value missing on the stack. { Offset = 0x12 } @@ -8892,12 +10315,16 @@ public static void Main() } "; - var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics( + var expected = // (18,28): error CS8178: A reference returned by a call to 'C.P.get' cannot be preserved across 'await' or 'yield' boundary. // Assign(second: ref P, first: await t); - Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "P").WithArguments("C.P.get").WithLocation(18, 28) - ); + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "P").WithArguments("C.P.get").WithLocation(18, 28); + + var comp = CreateCompilationWithMscorlib46(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -10669,6 +12096,7 @@ static async Task TestAIsNull() Console.WriteLine(""Before Assignment""); try { + // Sacrificial read should ensure that `await` never happens await Assign(a); } catch (NullReferenceException) @@ -10904,6 +12332,7 @@ static async Task TestAIsNull() Console.WriteLine(""Before Assignment""); try { + // Sacrificial read should ensure that `await` never happens await Assign(a); } catch (NullReferenceException) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index 6f4c06140b2..b1ef2381ff7 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -20,11 +20,9 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { + [CompilerTrait(CompilerFeature.Async)] public class CodeGenAsyncTests : EmitMetadataTestBase { - // https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible - private const MethodImplAttributes MethodImplOptionsAsync = (MethodImplAttributes)0x2000; - internal static string ExpectedOutput(string output, bool isRuntimeAsync = false) { return ExecutionConditionUtil.IsMonoOrCoreClr @@ -331,7 +329,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name)); } } @@ -375,11 +373,7 @@ public static async Task Main() {ReturnValueMissing("g__Impl|1_0", "0x7")} """ }, symbolValidator: verify); - verifier.VerifyDiagnostics( - // (13,25): 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 ValueTask Impl() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Impl").WithLocation(13, 25) - ); + verifier.VerifyDiagnostics(); verifier.VerifyIL("Test.F", """ { @@ -395,7 +389,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); Assert.Empty(test.GetTypeMembers()); } } @@ -458,7 +452,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name)); } } @@ -511,7 +505,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Empty(test.GetTypeMembers()); } } @@ -631,7 +625,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); if (!useValueTask && useGeneric) { AssertEx.Equal(["<>c"], test.GetTypeMembers().SelectAsArray(t => t.Name)); @@ -766,7 +760,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Empty(test.GetTypeMembers()); } } @@ -886,7 +880,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Empty(test.GetTypeMembers()); } } @@ -1018,7 +1012,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Empty(test.GetTypeMembers()); } } @@ -1129,7 +1123,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Empty(test.GetTypeMembers()); } } @@ -1237,7 +1231,7 @@ void verify(ModuleSymbol module) { var test = module.ContainingAssembly.GetTypeByMetadataName("Test"); var f = test.GetMethod("F"); - Assert.Equal(MethodImplOptionsAsync, f.ImplementationAttributes); + Assert.Equal(MethodImplAttributes.Async, f.ImplementationAttributes); AssertEx.Empty(test.GetTypeMembers()); } } @@ -2113,7 +2107,6 @@ public void AddressOf_Fixed() var source = """ using System.Threading.Tasks; // 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. - #pragma warning disable 1998 class Program { int F; @@ -2152,41 +2145,41 @@ struct S { public int F; } """; CreateCompilation(source, options: TestOptions.UnsafeDebugExe).VerifyDiagnostics( - // (11,20): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // (10,20): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer // int* ptr = &prog.F; // 1 - Diagnostic(ErrorCode.ERR_FixedNeeded, "&prog.F").WithLocation(11, 20), - // (15,26): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + Diagnostic(ErrorCode.ERR_FixedNeeded, "&prog.F").WithLocation(10, 20), + // (14,26): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. // int* localPtr = &local; // 2 - Diagnostic(ErrorCode.WRN_AddressOfInAsync, "local").WithLocation(15, 26), - // (16,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "local").WithLocation(14, 26), + // (15,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* localPtr1 = &local) { } // 3, 4 - Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&local").WithLocation(16, 33), - // (16,34): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&local").WithLocation(15, 33), + // (15,34): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. // fixed (int* localPtr1 = &local) { } // 3, 4 - Diagnostic(ErrorCode.WRN_AddressOfInAsync, "local").WithLocation(16, 34), - // (19,26): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "local").WithLocation(15, 34), + // (18,26): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. // int* innerPtr = &structLocal.F; // 5 - Diagnostic(ErrorCode.WRN_AddressOfInAsync, "structLocal.F").WithLocation(19, 26), - // (20,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "structLocal.F").WithLocation(18, 26), + // (19,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 - Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&structLocal.F").WithLocation(20, 33), - // (20,34): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&structLocal.F").WithLocation(19, 33), + // (19,34): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. // fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 - Diagnostic(ErrorCode.WRN_AddressOfInAsync, "structLocal.F").WithLocation(20, 34), - // (33,39): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "structLocal.F").WithLocation(19, 34), + // (32,39): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. // int* localFuncLocalPtr = &localFuncLocal; // 8 - Diagnostic(ErrorCode.WRN_AddressOfInAsync, "localFuncLocal").WithLocation(33, 39)); + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "localFuncLocal").WithLocation(32, 39)); CreateCompilation(source, options: TestOptions.UnsafeDebugExe.WithWarningLevel(7)).VerifyDiagnostics( - // (11,20): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // (10,20): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer // int* ptr = &prog.F; // 1 - Diagnostic(ErrorCode.ERR_FixedNeeded, "&prog.F").WithLocation(11, 20), - // (16,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + Diagnostic(ErrorCode.ERR_FixedNeeded, "&prog.F").WithLocation(10, 20), + // (15,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* localPtr1 = &local) { } // 3, 4 - Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&local").WithLocation(16, 33), - // (20,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&local").WithLocation(15, 33), + // (19,33): error CS0213: You cannot use the fixed statement to take the address of an already fixed expression // fixed (int* innerPtr1 = &structLocal.F) { } // 6, 7 - Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&structLocal.F").WithLocation(20, 33)); + Diagnostic(ErrorCode.ERR_FixedNotNeeded, "&structLocal.F").WithLocation(19, 33)); } [Fact] @@ -2361,6 +2354,28 @@ public static void Main() public static int Result = -1; }"; CompileAndVerify(source, expectedOutput: "0", options: TestOptions.UnsafeDebugExe, verify: Verification.Passes); + + var comp = CreateRuntimeAsyncCompilation(source); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [getBaseMyProp]: Unexpected type on the stack. { Offset = 0x11, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("TestClass.getBaseMyProp()", """ + { + // Code size 18 (0x12) + .maxstack 1 + IL_0000: ldc.i4.1 + IL_0001: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.Delay(int)" + IL_0006: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000b: ldarg.0 + IL_000c: call "int Base.MyProp.get" + IL_0011: ret + } + """); } [Fact] @@ -4885,9 +4900,6 @@ async void M() {} // CONSIDER: It would be nice if we didn't squiggle the whole method body, but this is a corner case. comp.VerifyEmitDiagnostics( - // (4,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 M() {} - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16), // (4,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncVoidMethodBuilder' is not defined or imported // async void M() {} Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "{}").WithArguments("System.Runtime.CompilerServices.AsyncVoidMethodBuilder").WithLocation(4, 20), @@ -4913,9 +4925,6 @@ async Task M() {} }"; var comp = CSharpTestBase.CreateEmptyCompilation(source, new[] { Net40.References.mscorlib }, TestOptions.ReleaseDll); // NOTE: 4.0, not 4.5, so it's missing the async helpers. comp.VerifyEmitDiagnostics( - // (4,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 Task M() {} - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16), // (4,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncTaskMethodBuilder' is not defined or imported // async Task M() {} Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "{}").WithArguments("System.Runtime.CompilerServices.AsyncTaskMethodBuilder").WithLocation(4, 20), @@ -4944,9 +4953,6 @@ class C }"; var comp = CSharpTestBase.CreateEmptyCompilation(source, new[] { Net40.References.mscorlib }, TestOptions.ReleaseDll); // NOTE: 4.0, not 4.5, so it's missing the async helpers. comp.VerifyEmitDiagnostics( - // (4,21): 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 Task F() => 3; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F").WithLocation(4, 21), // (4,25): error CS0518: Predefined type 'System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1' is not defined or imported // async Task F() => 3; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "=> 3").WithArguments("System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1").WithLocation(4, 25), @@ -7501,15 +7507,8 @@ private static ref long ReadMemory() } } "; - var diags = new[] - { - // (12,30): 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. - // public static async Task CompletedTask() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "CompletedTask").WithLocation(12, 30) - }; - - CompileAndVerify(source, options: TestOptions.DebugExe, verify: Verification.Skipped, expectedOutput: "0123").VerifyDiagnostics(diags); - CompileAndVerify(source, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: "0123").VerifyDiagnostics(diags); + CompileAndVerify(source, options: TestOptions.DebugExe, verify: Verification.Skipped, expectedOutput: "0123").VerifyDiagnostics(); + CompileAndVerify(source, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: "0123").VerifyDiagnostics(); } [Fact, WorkItem(40251, "https://github.com/dotnet/roslyn/issues/40251")] @@ -9185,7 +9184,6 @@ public static async Task Handler() } } - #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public static async Task Throw(int value) => throw new IntegerException(value); } @@ -9292,7 +9290,6 @@ public static Task Handler() } } - #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously public static async Task Throw(int value) => throw new IntegerException(value); } @@ -9645,9 +9642,6 @@ public static void M4() // (18,6): error CS9330: 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. // [MethodImpl(MethodImplOptions.Async)] Diagnostic(ErrorCode.ERR_MethodImplAttributeAsyncCannotBeUsed, "MethodImpl(MethodImplOptions.Async)").WithLocation(18, 6), - // (19,30): 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. - // public static async Task M3() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M3").WithLocation(19, 30), // (24,6): error CS9330: 'MethodImplAttribute.Async' cannot be manually applied to methods. Mark the method 'async'. // [MethodImpl(MethodImplOptions.Async | MethodImplOptions.Synchronized)] Diagnostic(ErrorCode.ERR_MethodImplAttributeAsyncCannotBeUsed, "MethodImpl(MethodImplOptions.Async | MethodImplOptions.Synchronized)").WithLocation(24, 6) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index f5b2774dc30..21249e80d70 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - [CompilerTrait(CompilerFeature.AsyncStreams)] + [CompilerTrait(CompilerFeature.AsyncStreams, CompilerFeature.Async)] public class CodeGenAwaitForeachTests : EmitMetadataTestBase { [Fact] @@ -4272,84 +4272,78 @@ public async ValueTask DisposeAsync() CompileAndVerify(comp, expectedOutput: expectedOutput); var runtimeAsyncComp = CreateRuntimeAsyncCompilation(source); - runtimeAsyncComp.VerifyEmitDiagnostics( - // (31,33): error CS9328: Method 'C.AsyncEnumerator.MoveNextAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async Task MoveNextAsync() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "MoveNextAsync").WithArguments("C.AsyncEnumerator.MoveNextAsync()").WithLocation(31, 33) - ); - // https://github.com/dotnet/roslyn/issues/79763 - // var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Main]: Return value missing on the stack. { Offset = 0x8c } - // [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } - // [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } - // """ - // }); - // verifier.VerifyIL("C.Main()", """ - // { - // // Code size 141 (0x8d) - // .maxstack 2 - // .locals init (C.AsyncEnumerator V_0, - // object V_1, - // int V_2, //i - // System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) - // IL_0000: newobj "C..ctor()" - // IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" - // IL_000a: stloc.0 - // IL_000b: ldnull - // IL_000c: stloc.1 - // .try - // { - // IL_000d: br.s IL_004b - // IL_000f: ldloca.s V_0 - // IL_0011: call "int C.AsyncEnumerator.Current.get" - // IL_0016: stloc.2 - // IL_0017: ldc.i4.6 - // IL_0018: ldc.i4.1 - // IL_0019: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" - // IL_001e: stloc.3 - // IL_001f: ldloca.s V_3 - // IL_0021: ldstr "Got(" - // IL_0026: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_002b: ldloca.s V_3 - // IL_002d: ldloc.2 - // IL_002e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" - // IL_0033: ldloca.s V_3 - // IL_0035: ldstr ") " - // IL_003a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_003f: ldloca.s V_3 - // IL_0041: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" - // IL_0046: call "void System.Console.Write(string)" - // IL_004b: ldloca.s V_0 - // IL_004d: call "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" - // IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" - // IL_0057: brtrue.s IL_000f - // IL_0059: leave.s IL_005e - // } - // catch object - // { - // IL_005b: stloc.1 - // IL_005c: leave.s IL_005e - // } - // IL_005e: ldloca.s V_0 - // IL_0060: call "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" - // IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" - // IL_006a: ldloc.1 - // IL_006b: brfalse.s IL_0082 - // IL_006d: ldloc.1 - // IL_006e: isinst "System.Exception" - // IL_0073: dup - // IL_0074: brtrue.s IL_0078 - // IL_0076: ldloc.1 - // IL_0077: throw - // IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" - // IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" - // IL_0082: ldstr "Done" - // IL_0087: call "void System.Console.Write(string)" - // IL_008c: ret - // } - // """); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x8c } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x65, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x2e } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 141 (0x8d) + .maxstack 2 + .locals init (C.AsyncEnumerator V_0, + object V_1, + int V_2, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3) + IL_0000: newobj "C..ctor()" + IL_0005: call "C.AsyncEnumerator C.GetAsyncEnumerator()" + IL_000a: stloc.0 + IL_000b: ldnull + IL_000c: stloc.1 + .try + { + IL_000d: br.s IL_004b + IL_000f: ldloca.s V_0 + IL_0011: call "int C.AsyncEnumerator.Current.get" + IL_0016: stloc.2 + IL_0017: ldc.i4.6 + IL_0018: ldc.i4.1 + IL_0019: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_001e: stloc.3 + IL_001f: ldloca.s V_3 + IL_0021: ldstr "Got(" + IL_0026: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_002b: ldloca.s V_3 + IL_002d: ldloc.2 + IL_002e: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_0033: ldloca.s V_3 + IL_0035: ldstr ") " + IL_003a: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_003f: ldloca.s V_3 + IL_0041: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_0046: call "void System.Console.Write(string)" + IL_004b: ldloca.s V_0 + IL_004d: call "System.Threading.Tasks.Task C.AsyncEnumerator.MoveNextAsync()" + IL_0052: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: brtrue.s IL_000f + IL_0059: leave.s IL_005e + } + catch object + { + IL_005b: stloc.1 + IL_005c: leave.s IL_005e + } + IL_005e: ldloca.s V_0 + IL_0060: call "System.Threading.Tasks.ValueTask C.AsyncEnumerator.DisposeAsync()" + IL_0065: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_006a: ldloc.1 + IL_006b: brfalse.s IL_0082 + IL_006d: ldloc.1 + IL_006e: isinst "System.Exception" + IL_0073: dup + IL_0074: brtrue.s IL_0078 + IL_0076: ldloc.1 + IL_0077: throw + IL_0078: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_007d: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_0082: ldstr "Done" + IL_0087: call "void System.Console.Write(string)" + IL_008c: ret + } + """); } [Fact] @@ -6447,92 +6441,84 @@ public async ValueTask DisposeAsync() expectedOutput: expectedOutput); var runtimeAsyncComp = CreateRuntimeAsyncCompilation(source); - runtimeAsyncComp.VerifyEmitDiagnostics( - // (32,38): error CS9328: Method 'C.AsyncEnumerator.MoveNextAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async ValueTask MoveNextAsync() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "MoveNextAsync").WithArguments("C.AsyncEnumerator.MoveNextAsync()").WithLocation(32, 38), - // (39,32): error CS9328: Method 'C.AsyncEnumerator.DisposeAsync()' uses a feature that is not supported by runtime async currently. Opt the method out of runtime async by attributing it with 'System.Runtime.CompilerServices.RuntimeAsyncMethodGenerationAttribute(false)'. - // public async ValueTask DisposeAsync() - Diagnostic(ErrorCode.ERR_UnsupportedFeatureInRuntimeAsync, "DisposeAsync").WithArguments("C.AsyncEnumerator.DisposeAsync()").WithLocation(39, 32) - ); - // var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with - // { - // ILVerifyMessage = """ - // [Main]: Return value missing on the stack. { Offset = 0x96 } - // [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x5d, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } - // [DisposeAsync]: Return value missing on the stack. { Offset = 0x5f } - // """ - // }); - // verifier.VerifyIL("C.Main()", """ - // { - // // Code size 151 (0x97) - // .maxstack 2 - // .locals init (System.Collections.Generic.IAsyncEnumerator V_0, - // System.Threading.CancellationToken V_1, - // object V_2, - // int V_3, //i - // System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) - // IL_0000: newobj "C..ctor()" - // IL_0005: ldloca.s V_1 - // IL_0007: initobj "System.Threading.CancellationToken" - // IL_000d: ldloc.1 - // IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" - // IL_0013: stloc.0 - // IL_0014: ldnull - // IL_0015: stloc.2 - // .try - // { - // IL_0016: br.s IL_0054 - // IL_0018: ldloc.0 - // IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" - // IL_001e: stloc.3 - // IL_001f: ldc.i4.6 - // IL_0020: ldc.i4.1 - // IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" - // IL_0026: stloc.s V_4 - // IL_0028: ldloca.s V_4 - // IL_002a: ldstr "Got(" - // IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_0034: ldloca.s V_4 - // IL_0036: ldloc.3 - // IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" - // IL_003c: ldloca.s V_4 - // IL_003e: ldstr ") " - // IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" - // IL_0048: ldloca.s V_4 - // IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" - // IL_004f: call "void System.Console.Write(string)" - // IL_0054: ldloc.0 - // IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" - // IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" - // IL_005f: brtrue.s IL_0018 - // IL_0061: leave.s IL_0066 - // } - // catch object - // { - // IL_0063: stloc.2 - // IL_0064: leave.s IL_0066 - // } - // IL_0066: ldloc.0 - // IL_0067: brfalse.s IL_0074 - // IL_0069: ldloc.0 - // IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" - // IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" - // IL_0074: ldloc.2 - // IL_0075: brfalse.s IL_008c - // IL_0077: ldloc.2 - // IL_0078: isinst "System.Exception" - // IL_007d: dup - // IL_007e: brtrue.s IL_0082 - // IL_0080: ldloc.2 - // IL_0081: throw - // IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" - // IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" - // IL_008c: ldstr "Done" - // IL_0091: call "void System.Console.Write(string)" - // IL_0096: ret - // } - // """); + var verifier = CompileAndVerify(runtimeAsyncComp, expectedOutput: CodeGenAsyncTests.ExpectedOutput(expectedOutput, isRuntimeAsync: true), verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x96 } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x65, Found = Int32, Expected = value '[System.Runtime]System.Threading.Tasks.ValueTask`1' } + [DisposeAsync]: Return value missing on the stack. { Offset = 0x66 } + """ + }); + verifier.VerifyIL("C.Main()", """ + { + // Code size 151 (0x97) + .maxstack 2 + .locals init (System.Collections.Generic.IAsyncEnumerator V_0, + System.Threading.CancellationToken V_1, + object V_2, + int V_3, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: newobj "C..ctor()" + IL_0005: ldloca.s V_1 + IL_0007: initobj "System.Threading.CancellationToken" + IL_000d: ldloc.1 + IL_000e: callvirt "System.Collections.Generic.IAsyncEnumerator System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_0013: stloc.0 + IL_0014: ldnull + IL_0015: stloc.2 + .try + { + IL_0016: br.s IL_0054 + IL_0018: ldloc.0 + IL_0019: callvirt "int System.Collections.Generic.IAsyncEnumerator.Current.get" + IL_001e: stloc.3 + IL_001f: ldc.i4.6 + IL_0020: ldc.i4.1 + IL_0021: newobj "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)" + IL_0026: stloc.s V_4 + IL_0028: ldloca.s V_4 + IL_002a: ldstr "Got(" + IL_002f: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0034: ldloca.s V_4 + IL_0036: ldloc.3 + IL_0037: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)" + IL_003c: ldloca.s V_4 + IL_003e: ldstr ") " + IL_0043: call "void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)" + IL_0048: ldloca.s V_4 + IL_004a: call "string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()" + IL_004f: call "void System.Console.Write(string)" + IL_0054: ldloc.0 + IL_0055: callvirt "System.Threading.Tasks.ValueTask System.Collections.Generic.IAsyncEnumerator.MoveNextAsync()" + IL_005a: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_005f: brtrue.s IL_0018 + IL_0061: leave.s IL_0066 + } + catch object + { + IL_0063: stloc.2 + IL_0064: leave.s IL_0066 + } + IL_0066: ldloc.0 + IL_0067: brfalse.s IL_0074 + IL_0069: ldloc.0 + IL_006a: callvirt "System.Threading.Tasks.ValueTask System.IAsyncDisposable.DisposeAsync()" + IL_006f: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.ValueTask)" + IL_0074: ldloc.2 + IL_0075: brfalse.s IL_008c + IL_0077: ldloc.2 + IL_0078: isinst "System.Exception" + IL_007d: dup + IL_007e: brtrue.s IL_0082 + IL_0080: ldloc.2 + IL_0081: throw + IL_0082: call "System.Runtime.ExceptionServices.ExceptionDispatchInfo System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(System.Exception)" + IL_0087: callvirt "void System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()" + IL_008c: ldstr "Done" + IL_0091: call "void System.Console.Write(string)" + IL_0096: ret + } + """); } [Fact, WorkItem(27651, "https://github.com/dotnet/roslyn/issues/27651")] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs index 942cc36a9d9..b47a482494d 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitUsingTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { - [CompilerTrait(CompilerFeature.AsyncStreams)] + [CompilerTrait(CompilerFeature.AsyncStreams, CompilerFeature.Async)] public class CodeGenAwaitUsingTests : CSharpTestBase { [Fact] @@ -172,10 +172,7 @@ public async Task DisposeAsync() { } comp.VerifyDiagnostics( // (3,11): error CS0738: 'C' does not implement interface member 'IAsyncDisposable.DisposeAsync()'. 'C.DisposeAsync()' cannot implement 'IAsyncDisposable.DisposeAsync()' because it does not have the matching return type of 'ValueTask'. // class C : System.IAsyncDisposable - Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "System.IAsyncDisposable").WithArguments("C", "System.IAsyncDisposable.DisposeAsync()", "C.DisposeAsync()", "System.Threading.Tasks.ValueTask").WithLocation(3, 11), - // (9,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. - // public async Task DisposeAsync() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "DisposeAsync").WithLocation(9, 23) + Diagnostic(ErrorCode.ERR_CloseUnimplementedInterfaceMemberWrongReturnType, "System.IAsyncDisposable").WithArguments("C", "System.IAsyncDisposable.DisposeAsync()", "C.DisposeAsync()", "System.Threading.Tasks.ValueTask").WithLocation(3, 11) ); } @@ -555,11 +552,7 @@ async System.Threading.Tasks.Task local() } "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); - comp.VerifyDiagnostics( - // (4,53): 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. - // public static async System.Threading.Tasks.Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(4, 53) - ); + comp.VerifyDiagnostics(); } [Fact] @@ -598,14 +591,7 @@ async System.Threading.Tasks.Task innerLocal() } "; var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); - comp.VerifyDiagnostics( - // (17,43): 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 System.Threading.Tasks.Task local() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "local").WithLocation(17, 43), - // (6,42): 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. - // System.Action lambda1 = async () => - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(6, 42) - ); + comp.VerifyDiagnostics(); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs index a86ab7c3426..7b9691dbc38 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenFunctionPointersTests.cs @@ -6458,8 +6458,7 @@ protected override void M7(delegate* ptr) {} protected override delegate* M8() => throw null; }"); - comp.VerifyDiagnostics( - ); + comp.VerifyDiagnostics(); assertMethods(comp.SourceModule); CompileAndVerify(comp, symbolValidator: assertMethods); @@ -6511,8 +6510,7 @@ protected override void M7(delegate* ptr) {} protected override delegate* M8() => throw null; }"); - comp.VerifyDiagnostics( - ); + comp.VerifyDiagnostics(); assertMethods(comp.SourceModule); CompileAndVerify(comp, symbolValidator: assertMethods, verify: Verification.Skipped); @@ -9799,18 +9797,12 @@ public static async Task Main() {} // (6,30): error CS0017: Program has more than one entry point defined. Compile with /main to specify the type that contains the entry point. // public static async Task Main() {} Diagnostic(ErrorCode.ERR_MultipleEntryPoints, "Main").WithLocation(6, 30), - // (6,30): 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. - // public static async Task Main() {} - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(6, 30), // (11,25): error CS8894: Cannot use 'Task' as a return type on a method attributed with 'UnmanagedCallersOnly'. // public static async Task Main() {} Diagnostic(ErrorCode.ERR_CannotUseManagedTypeInUnmanagedCallersOnly, "Task").WithArguments("System.Threading.Tasks.Task", "return").WithLocation(11, 25), // (11,30): error CS8899: Application entry points cannot be attributed with 'UnmanagedCallersOnly'. // public static async Task Main() {} - Diagnostic(ErrorCode.ERR_EntryPointCannotBeUnmanagedCallersOnly, "Main").WithLocation(11, 30), - // (11,30): 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. - // public static async Task Main() {} - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(11, 30) + Diagnostic(ErrorCode.ERR_EntryPointCannotBeUnmanagedCallersOnly, "Main").WithLocation(11, 30) ); } @@ -9837,10 +9829,7 @@ public static async Task Main() {} Diagnostic(ErrorCode.ERR_CannotUseManagedTypeInUnmanagedCallersOnly, "Task").WithArguments("System.Threading.Tasks.Task", "return").WithLocation(11, 25), // (11,30): warning CS8892: Method 'D.Main()' will not be used as an entry point because a synchronous entry point 'C.Main()' was found. // public static async Task Main() {} - Diagnostic(ErrorCode.WRN_SyncAndAsyncEntryPoints, "Main").WithArguments("D.Main()", "C.Main()").WithLocation(11, 30), - // (11,30): 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. - // public static async Task Main() {} - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(11, 30) + Diagnostic(ErrorCode.WRN_SyncAndAsyncEntryPoints, "Main").WithArguments("D.Main()", "C.Main()").WithLocation(11, 30) ); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs index eb8319ef8c9..681dc2e8e99 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefConditionalOperatorTests.cs @@ -526,13 +526,17 @@ static async Task One() } }"; - var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics( + var expected = // (16,10): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // (b? ref val1: ref val2) += await One(); - Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10) - ); + Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10); + + var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -569,15 +573,18 @@ static async Task One() return 1; } - }"; - var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics( + var expected = // (16,10): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // (b? ref val1: ref val2) = await One(); - Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10) - ); + Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b? ref val1: ref val2").WithLocation(16, 10); + + var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] @@ -2290,16 +2297,20 @@ static async Task GetC(C c) } "; - var comp = CreateCompilation(source); - - comp.VerifyEmitDiagnostics( + var expected = new[] { // (27,18): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // Test(ref b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F, await GetC(new C())); Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b ? ref (await GetC(c1)).F : ref (await GetC(c2)).F").WithLocation(27, 18), // (28,18): error CS8325: 'await' cannot be used in an expression containing a ref conditional operator // Test(ref b ? ref c1.F : ref c2.F, await GetC(new C())); Diagnostic(ErrorCode.ERR_RefConditionalAndAwait, "b ? ref c1.F : ref c2.F").WithLocation(28, 18) - ); + }; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(source); + comp.VerifyEmitDiagnostics(expected); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs index 989fda64284..7c0e6b10362 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenRefLocalTests.cs @@ -4781,5 +4781,75 @@ .maxstack 0 IL_0000: ret }"); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79867")] + public void StackOverflow_01() + { + var source = +@" +public class Parent +{ + Child[] _children = new Child[100]; + + public void BrokenMethod() + { + ref var itemRef = ref _children[0]; + } +} + +public record class Child(Parent> Parent) { } +"; + CompileAndVerify(source + IsExternalInitTypeDefinition, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79867")] + public void StackOverflow_02() + { + var source = +@" +public struct Parent +{ + Child[] _children; + + public void BrokenMethod() + { + ref var itemRef = ref _children[0]; + } +} + +public record struct Child(Parent> Parent) { } +"; + CompileAndVerify(source + IsExternalInitTypeDefinition, verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79867")] + public void StackOverflow_03() + { + var source = +@" +public struct Parent +{ + Child _children; + + public void BrokenMethod() + { + ref var itemRef = ref _children; + } +} + +public record struct Child(Parent> Parent) { } +"; + CreateCompilation(source + IsExternalInitTypeDefinition).VerifyEmitDiagnostics( + // (4,14): error CS0523: Struct member 'Parent._children' of type 'Child' causes a cycle in the struct layout + // Child _children; + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "_children").WithArguments("Parent._children", "Child").WithLocation(4, 14), + // (12,48): error CS0523: Struct member 'Child.Parent' of type 'Parent>' causes a cycle in the struct layout + // public record struct Child(Parent> Parent) { } + Diagnostic(ErrorCode.ERR_StructLayoutCycle, "Parent").WithArguments("Child.Parent", "Parent>").WithLocation(12, 48) + ); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs index f3538e88440..0c33fb356fb 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs @@ -5817,7 +5817,6 @@ public T M() public void ConditionalInAsyncTask() { var source = @" -#pragma warning disable CS1998 // suppress 'no await in async' warning using System; using System.Threading.Tasks; diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTryFinally.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTryFinally.cs index 1dc1c2eaf63..74e8d387568 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTryFinally.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTryFinally.cs @@ -4276,9 +4276,6 @@ async Task M() var expectedDiagnostics = new[] { - // (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 Task M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(6, 16), // (10,9): warning CS0162: Unreachable code detected // try Diagnostic(ErrorCode.WRN_UnreachableCode, "try").WithLocation(10, 9) @@ -4311,11 +4308,7 @@ async Task M() } """; - CompileAndVerify(source, options: TestOptions.ReleaseDll).VerifyDiagnostics( - // (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 Task M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(6, 16) - ); + CompileAndVerify(source, options: TestOptions.ReleaseDll).VerifyDiagnostics(); } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs index 86e1ed704db..74ae46d3796 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs @@ -4540,10 +4540,7 @@ static async Task Main() "; var expectedOutput = "correct"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - // (7,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 Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(7, 23)); + compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } @@ -4574,10 +4571,7 @@ static async Task Main() "; var expectedOutput = "correct"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - // (7,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 Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(7, 23)); + compilation.VerifyDiagnostics(); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs index d524096891b..336be79f4e3 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs @@ -34306,8 +34306,119 @@ static async Task Get0() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x77 } + [Test2]: Return value missing on the stack. { Offset = 0x2b } + [Test3]: Return value missing on the stack. { Offset = 0x21 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 120 (0x78) + .maxstack 3 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: initobj "T" + IL_0031: ldloc.3 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: ldloc.1 + IL_0043: constrained. "T" + IL_0049: callvirt "int I1.this[int].get" + IL_004e: ldc.i4.1 + IL_004f: add + IL_0050: stloc.2 + IL_0051: ldloca.s V_3 + IL_0053: initobj "T" + IL_0059: ldloc.3 + IL_005a: box "T" + IL_005f: brtrue.s IL_0065 + IL_0061: ldloca.s V_0 + IL_0063: br.s IL_006a + IL_0065: ldsflda "T Program.F" + IL_006a: ldloc.1 + IL_006b: ldloc.2 + IL_006c: constrained. "T" + IL_0072: callvirt "void I1.this[int].set" + IL_0077: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "T Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: box "T" + IL_0016: ldloc.0 + IL_0017: callvirt "int I1.this[int].get" + IL_001c: ldc.i4.1 + IL_001d: add + IL_001e: stloc.1 + IL_001f: box "T" + IL_0024: ldloc.0 + IL_0025: ldloc.1 + IL_0026: callvirt "void I1.this[int].set" + IL_002b: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 34 (0x22) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "C1 Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: ldloc.0 + IL_0012: callvirt "int C1.this[int].get" + IL_0017: ldc.i4.1 + IL_0018: add + IL_0019: stloc.1 + IL_001a: ldloc.0 + IL_001b: ldloc.1 + IL_001c: callvirt "void C1.this[int].set" + IL_0021: ret + } + """); } [Fact] @@ -34601,8 +34712,119 @@ static async Task Get0() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x77 } + [Test2]: Return value missing on the stack. { Offset = 0x2b } + [Test3]: Return value missing on the stack. { Offset = 0x21 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 120 (0x78) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: initobj "T" + IL_0031: ldloc.3 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: ldloc.1 + IL_0043: constrained. "T" + IL_0049: callvirt "int I1.this[int].get" + IL_004e: stloc.2 + IL_004f: ldloca.s V_3 + IL_0051: initobj "T" + IL_0057: ldloc.3 + IL_0058: box "T" + IL_005d: brtrue.s IL_0063 + IL_005f: ldloca.s V_0 + IL_0061: br.s IL_0068 + IL_0063: ldsflda "T Program.F" + IL_0068: ldloc.1 + IL_0069: ldloc.2 + IL_006a: ldc.i4.1 + IL_006b: add + IL_006c: constrained. "T" + IL_0072: callvirt "void I1.this[int].set" + IL_0077: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 44 (0x2c) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "T Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: box "T" + IL_0016: ldloc.0 + IL_0017: callvirt "int I1.this[int].get" + IL_001c: stloc.1 + IL_001d: box "T" + IL_0022: ldloc.0 + IL_0023: ldloc.1 + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: callvirt "void I1.this[int].set" + IL_002b: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 34 (0x22) + .maxstack 4 + .locals init (int V_0, + int V_1) + IL_0000: ldsfld "C1 Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: ldloc.0 + IL_0012: callvirt "int C1.this[int].get" + IL_0017: stloc.1 + IL_0018: ldloc.0 + IL_0019: ldloc.1 + IL_001a: ldc.i4.1 + IL_001b: add + IL_001c: callvirt "void C1.this[int].set" + IL_0021: ret + } + """); } [Fact] @@ -34973,8 +35195,116 @@ static async Task Get0() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x75 } + [Test2]: Return value missing on the stack. { Offset = 0x2b } + [Test3]: Return value missing on the stack. { Offset = 0x21 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 118 (0x76) + .maxstack 4 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: ldloc.1 + IL_0043: ldloca.s V_2 + IL_0045: initobj "T" + IL_004b: ldloc.2 + IL_004c: box "T" + IL_0051: brtrue.s IL_0057 + IL_0053: ldloca.s V_0 + IL_0055: br.s IL_005c + IL_0057: ldsflda "T Program.F" + IL_005c: ldloc.1 + IL_005d: constrained. "T" + IL_0063: callvirt "int I1.this[int].get" + IL_0068: ldc.i4.1 + IL_0069: add + IL_006a: constrained. "T" + IL_0070: callvirt "void I1.this[int].set" + IL_0075: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 44 (0x2c) + .maxstack 4 + .locals init (T V_0, + int V_1) + IL_0000: ldsfld "T Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: box "T" + IL_0017: ldloc.1 + IL_0018: ldloc.0 + IL_0019: box "T" + IL_001e: ldloc.1 + IL_001f: callvirt "int I1.this[int].get" + IL_0024: ldc.i4.1 + IL_0025: add + IL_0026: callvirt "void I1.this[int].set" + IL_002b: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 34 (0x22) + .maxstack 4 + .locals init (C1 V_0, + int V_1) + IL_0000: ldsfld "C1 Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: ldloc.1 + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: callvirt "int C1.this[int].get" + IL_001a: ldc.i4.1 + IL_001b: add + IL_001c: callvirt "void C1.this[int].set" + IL_0021: ret + } + """); } [Fact] @@ -35057,8 +35387,129 @@ static async Task Get1() } """; + var expectedOutput = "123123125:123123125:123123125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123125:123123125:123123125").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x77 } + [Test2]: Return value missing on the stack. { Offset = 0x36 } + [Test3]: Return value missing on the stack. { Offset = 0x23 } + [Get1]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 120 (0x78) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0032 + IL_002e: ldloca.s V_0 + IL_0030: br.s IL_0037 + IL_0032: ldsflda "T Program.F" + IL_0037: ldc.i4.0 + IL_0038: constrained. "T" + IL_003e: callvirt "int I1.this[int].get" + IL_0043: stloc.1 + IL_0044: call "System.Threading.Tasks.Task Program.Get1()" + IL_0049: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_004e: stloc.2 + IL_004f: ldloca.s V_3 + IL_0051: initobj "T" + IL_0057: ldloc.3 + IL_0058: box "T" + IL_005d: brtrue.s IL_0063 + IL_005f: ldloca.s V_0 + IL_0061: br.s IL_0068 + IL_0063: ldsflda "T Program.F" + IL_0068: ldc.i4.0 + IL_0069: ldloc.1 + IL_006a: ldloc.2 + IL_006b: add + IL_006c: constrained. "T" + IL_0072: callvirt "void I1.this[int].set" + IL_0077: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 55 (0x37) + .maxstack 4 + .locals init (T V_0, + T V_1, + int V_2, + int V_3) + IL_0000: ldsfld "T Program.F" + IL_0005: stloc.0 + IL_0006: ldloca.s V_0 + IL_0008: dup + IL_0009: ldobj "T" + IL_000e: stloc.1 + IL_000f: ldc.i4.0 + IL_0010: constrained. "T" + IL_0016: callvirt "int I1.this[int].get" + IL_001b: stloc.2 + IL_001c: call "System.Threading.Tasks.Task Program.Get1()" + IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0026: stloc.3 + IL_0027: ldloc.1 + IL_0028: box "T" + IL_002d: ldc.i4.0 + IL_002e: ldloc.2 + IL_002f: ldloc.3 + IL_0030: add + IL_0031: callvirt "void I1.this[int].set" + IL_0036: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 36 (0x24) + .maxstack 4 + .locals init (C1 V_0, + int V_1, + int V_2) + IL_0000: ldsfld "C1 Program.F" + IL_0005: dup + IL_0006: stloc.0 + IL_0007: ldc.i4.0 + IL_0008: callvirt "int C1.this[int].get" + IL_000d: stloc.1 + IL_000e: call "System.Threading.Tasks.Task Program.Get1()" + IL_0013: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0018: stloc.2 + IL_0019: ldloc.0 + IL_001a: ldc.i4.0 + IL_001b: ldloc.1 + IL_001c: ldloc.2 + IL_001d: add + IL_001e: callvirt "void C1.this[int].set" + IL_0023: ret + } + """); } [Fact] @@ -35148,8 +35599,147 @@ static async Task Get1() } """; + var expectedOutput = "123123126:123123126:123123126"; var comp = CreateCompilation(src, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123126:123123126:123123126").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x89 } + [Test2]: Return value missing on the stack. { Offset = 0x3c } + [Test3]: Return value missing on the stack. { Offset = 0x32 } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + [Get1]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 138 (0x8a) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldloca.s V_5 + IL_0002: initobj "T" + IL_0008: ldloc.s V_5 + IL_000a: box "T" + IL_000f: brtrue.s IL_0019 + IL_0011: ldsfld "T Program.F" + IL_0016: stloc.0 + IL_0017: br.s IL_001f + IL_0019: ldsfld "T Program.F" + IL_001e: pop + IL_001f: call "System.Threading.Tasks.Task Program.Get0()" + IL_0024: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0029: stloc.1 + IL_002a: ldloc.1 + IL_002b: stloc.2 + IL_002c: ldloca.s V_5 + IL_002e: initobj "T" + IL_0034: ldloc.s V_5 + IL_0036: box "T" + IL_003b: brtrue.s IL_0041 + IL_003d: ldloca.s V_0 + IL_003f: br.s IL_0046 + IL_0041: ldsflda "T Program.F" + IL_0046: ldloc.1 + IL_0047: constrained. "T" + IL_004d: callvirt "int I1.this[int].get" + IL_0052: stloc.3 + IL_0053: call "System.Threading.Tasks.Task Program.Get1()" + IL_0058: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_005d: stloc.s V_4 + IL_005f: ldloca.s V_5 + IL_0061: initobj "T" + IL_0067: ldloc.s V_5 + IL_0069: box "T" + IL_006e: brtrue.s IL_0074 + IL_0070: ldloca.s V_0 + IL_0072: br.s IL_0079 + IL_0074: ldsflda "T Program.F" + IL_0079: ldloc.2 + IL_007a: ldloc.3 + IL_007b: ldloc.s V_4 + IL_007d: add + IL_007e: constrained. "T" + IL_0084: callvirt "void I1.this[int].set" + IL_0089: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 61 (0x3d) + .maxstack 4 + .locals init (int V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "T Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: stloc.1 + IL_0012: ldloc.0 + IL_0013: stloc.2 + IL_0014: box "T" + IL_0019: ldloc.0 + IL_001a: callvirt "int I1.this[int].get" + IL_001f: stloc.3 + IL_0020: call "System.Threading.Tasks.Task Program.Get1()" + IL_0025: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002a: stloc.s V_4 + IL_002c: ldloc.1 + IL_002d: box "T" + IL_0032: ldloc.2 + IL_0033: ldloc.3 + IL_0034: ldloc.s V_4 + IL_0036: add + IL_0037: callvirt "void I1.this[int].set" + IL_003c: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 51 (0x33) + .maxstack 4 + .locals init (int V_0, + C1 V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "C1 Program.F" + IL_0005: call "System.Threading.Tasks.Task Program.Get0()" + IL_000a: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000f: stloc.0 + IL_0010: dup + IL_0011: stloc.1 + IL_0012: ldloc.0 + IL_0013: stloc.2 + IL_0014: ldloc.0 + IL_0015: callvirt "int C1.this[int].get" + IL_001a: stloc.3 + IL_001b: call "System.Threading.Tasks.Task Program.Get1()" + IL_0020: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0025: stloc.s V_4 + IL_0027: ldloc.1 + IL_0028: ldloc.2 + IL_0029: ldloc.3 + IL_002a: ldloc.s V_4 + IL_002c: add + IL_002d: callvirt "void C1.this[int].set" + IL_0032: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] @@ -35480,8 +36070,143 @@ static async Task Get0() } """; + var expectedOutput = "123123126:123123126:123123126"; var comp = CreateCompilation(src, targetFramework: TargetFramework.NetLatest, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123126:123123126:123123126").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0x9c } + [Test2]: Return value missing on the stack. { Offset = 0x39 } + [Test3]: Return value missing on the stack. { Offset = 0x2a } + [Get0]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 157 (0x9d) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get0()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_3 + IL_002b: initobj "T" + IL_0031: ldloc.3 + IL_0032: box "T" + IL_0037: brtrue.s IL_003d + IL_0039: ldloca.s V_0 + IL_003b: br.s IL_0042 + IL_003d: ldsflda "T Program.F" + IL_0042: constrained. "T" + IL_0048: callvirt "int I1.Length.get" + IL_004d: ldloc.1 + IL_004e: sub + IL_004f: stloc.2 + IL_0050: ldloca.s V_3 + IL_0052: initobj "T" + IL_0058: ldloc.3 + IL_0059: box "T" + IL_005e: brtrue.s IL_0064 + IL_0060: ldloca.s V_0 + IL_0062: br.s IL_0069 + IL_0064: ldsflda "T Program.F" + IL_0069: ldloc.2 + IL_006a: ldloca.s V_3 + IL_006c: initobj "T" + IL_0072: ldloc.3 + IL_0073: box "T" + IL_0078: brtrue.s IL_007e + IL_007a: ldloca.s V_0 + IL_007c: br.s IL_0083 + IL_007e: ldsflda "T Program.F" + IL_0083: ldloc.2 + IL_0084: constrained. "T" + IL_008a: callvirt "int I1.this[int].get" + IL_008f: ldc.i4.1 + IL_0090: add + IL_0091: constrained. "T" + IL_0097: callvirt "void I1.this[int].set" + IL_009c: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 58 (0x3a) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2) + IL_0000: ldsfld "T Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: box "T" + IL_0017: callvirt "int I1.Length.get" + IL_001c: ldloc.1 + IL_001d: sub + IL_001e: stloc.2 + IL_001f: ldloc.0 + IL_0020: box "T" + IL_0025: ldloc.2 + IL_0026: ldloc.0 + IL_0027: box "T" + IL_002c: ldloc.2 + IL_002d: callvirt "int I1.this[int].get" + IL_0032: ldc.i4.1 + IL_0033: add + IL_0034: callvirt "void I1.this[int].set" + IL_0039: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 43 (0x2b) + .maxstack 4 + .locals init (C1 V_0, + int V_1, + int V_2) + IL_0000: ldsfld "C1 Program.F" + IL_0005: stloc.0 + IL_0006: call "System.Threading.Tasks.Task Program.Get0()" + IL_000b: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: callvirt "int C1.Length.get" + IL_0017: ldloc.1 + IL_0018: sub + IL_0019: stloc.2 + IL_001a: ldloc.0 + IL_001b: ldloc.2 + IL_001c: ldloc.0 + IL_001d: ldloc.2 + IL_001e: callvirt "int C1.this[int].get" + IL_0023: ldc.i4.1 + IL_0024: add + IL_0025: callvirt "void C1.this[int].set" + IL_002a: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] @@ -35574,8 +36299,155 @@ static async Task Get1() } """; + var expectedOutput = "123123126:123123126:123123126"; var comp = CreateCompilation(src, targetFramework: TargetFramework.NetLatest, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "123123126:123123126:123123126").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test1]: Return value missing on the stack. { Offset = 0xa4 } + [Test2]: Return value missing on the stack. { Offset = 0x3d } + [Test3]: Return value missing on the stack. { Offset = 0x2e } + [Get1]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test1()", """ + { + // Code size 165 (0xa5) + .maxstack 4 + .locals init (T V_0, + int V_1, + int V_2, + int V_3, + int V_4, + T V_5) + IL_0000: ldloca.s V_5 + IL_0002: initobj "T" + IL_0008: ldloc.s V_5 + IL_000a: box "T" + IL_000f: brtrue.s IL_0019 + IL_0011: ldsfld "T Program.F" + IL_0016: stloc.0 + IL_0017: br.s IL_001f + IL_0019: ldsfld "T Program.F" + IL_001e: pop + IL_001f: ldloca.s V_5 + IL_0021: initobj "T" + IL_0027: ldloc.s V_5 + IL_0029: box "T" + IL_002e: brtrue.s IL_0034 + IL_0030: ldloca.s V_0 + IL_0032: br.s IL_0039 + IL_0034: ldsflda "T Program.F" + IL_0039: constrained. "T" + IL_003f: callvirt "int I1.Length.get" + IL_0044: stloc.1 + IL_0045: ldloc.1 + IL_0046: stloc.2 + IL_0047: ldloca.s V_5 + IL_0049: initobj "T" + IL_004f: ldloc.s V_5 + IL_0051: box "T" + IL_0056: brtrue.s IL_005c + IL_0058: ldloca.s V_0 + IL_005a: br.s IL_0061 + IL_005c: ldsflda "T Program.F" + IL_0061: ldloc.1 + IL_0062: constrained. "T" + IL_0068: callvirt "int I1.this[int].get" + IL_006d: stloc.3 + IL_006e: call "System.Threading.Tasks.Task Program.Get1()" + IL_0073: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0078: stloc.s V_4 + IL_007a: ldloca.s V_5 + IL_007c: initobj "T" + IL_0082: ldloc.s V_5 + IL_0084: box "T" + IL_0089: brtrue.s IL_008f + IL_008b: ldloca.s V_0 + IL_008d: br.s IL_0094 + IL_008f: ldsflda "T Program.F" + IL_0094: ldloc.2 + IL_0095: ldloc.3 + IL_0096: ldloc.s V_4 + IL_0098: add + IL_0099: constrained. "T" + IL_009f: callvirt "void I1.this[int].set" + IL_00a4: ret + } + """); + verifier.VerifyIL("Program.Test2()", """ + { + // Code size 62 (0x3e) + .maxstack 4 + .locals init (int V_0, + T V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "T Program.F" + IL_0005: dup + IL_0006: box "T" + IL_000b: callvirt "int I1.Length.get" + IL_0010: stloc.0 + IL_0011: dup + IL_0012: stloc.1 + IL_0013: ldloc.0 + IL_0014: stloc.2 + IL_0015: box "T" + IL_001a: ldloc.0 + IL_001b: callvirt "int I1.this[int].get" + IL_0020: stloc.3 + IL_0021: call "System.Threading.Tasks.Task Program.Get1()" + IL_0026: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_002b: stloc.s V_4 + IL_002d: ldloc.1 + IL_002e: box "T" + IL_0033: ldloc.2 + IL_0034: ldloc.3 + IL_0035: ldloc.s V_4 + IL_0037: add + IL_0038: callvirt "void I1.this[int].set" + IL_003d: ret + } + """); + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 47 (0x2f) + .maxstack 4 + .locals init (int V_0, + C1 V_1, + int V_2, + int V_3, + int V_4) + IL_0000: ldsfld "C1 Program.F" + IL_0005: dup + IL_0006: callvirt "int C1.Length.get" + IL_000b: stloc.0 + IL_000c: dup + IL_000d: stloc.1 + IL_000e: ldloc.0 + IL_000f: stloc.2 + IL_0010: ldloc.0 + IL_0011: callvirt "int C1.this[int].get" + IL_0016: stloc.3 + IL_0017: call "System.Threading.Tasks.Task Program.Get1()" + IL_001c: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0021: stloc.s V_4 + IL_0023: ldloc.1 + IL_0024: ldloc.2 + IL_0025: ldloc.3 + IL_0026: ldloc.s V_4 + IL_0028: add + IL_0029: callvirt "void C1.this[int].set" + IL_002e: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index a79819fbe93..a3b0da3b6e0 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -28,6 +28,7 @@ + @@ -35,4 +36,4 @@ - \ No newline at end of file + diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs index 80b439bf2df..3ed3b387899 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests.cs @@ -11648,6 +11648,106 @@ public void AvoidCascadingDiagnosticsOnMissingAttribute() Assert.Equal("G = 0 switch { _ => 1 }", thirdArgument.ToString()); Assert.Equal("System.Int32", model.GetTypeInfo(thirdArgument.Expression).Type.ToTestDisplayString()); } + + [Fact] + public void MetadataUpdateDeletedAttribute_ErrorWhenManuallyApplied() + { + string source = @" +using System; +using System.Runtime.CompilerServices; + +[assembly: MetadataUpdateDeleted] +[module: MetadataUpdateDeleted] + +[MetadataUpdateDeleted] class TestClass<[MetadataUpdateDeleted] T> +{ + [MetadataUpdateDeleted] public TestClass() + { + field = 1; + Event?.Invoke(); + } + + [MetadataUpdateDeleted] public int field; + [MetadataUpdateDeleted] public int Property + { + [MetadataUpdateDeleted] get; + [MetadataUpdateDeleted] set; + } + [MetadataUpdateDeleted] public int this[int a] { get => a; } + [MetadataUpdateDeleted] [return: MetadataUpdateDeleted] public int Method( + [MetadataUpdateDeleted] int x) => x; + + [MetadataUpdateDeleted] public event Action Event; + [MetadataUpdateDeleted] public event Action Event2 + { + [MetadataUpdateDeleted] add {} + [MetadataUpdateDeleted] remove {} + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] + public sealed class MetadataUpdateDeletedAttribute : Attribute + { + } +} +"; + var compilation = CreateCompilation(source); + compilation.VerifyDiagnostics( + // (5,12): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [assembly: MetadataUpdateDeleted] + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(5, 12), + // (6,10): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [module: MetadataUpdateDeleted] + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(6, 10), + // (8,2): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] class TestClass<[MetadataUpdateDeleted] T> + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(8, 2), + // (8,42): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] class TestClass<[MetadataUpdateDeleted] T> + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(8, 42), + // (10,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] public TestClass() + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(10, 6), + // (16,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] public int field; + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(16, 6), + // (17,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] public int Property + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(17, 6), + // (19,10): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] get; + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(19, 10), + // (20,10): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] set; + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(20, 10), + // (22,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] public int this[int a] { get => a; } + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(22, 6), + // (23,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] [return: MetadataUpdateDeleted] public int Method( + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(23, 6), + // (23,38): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] [return: MetadataUpdateDeleted] public int Method( + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(23, 38), + // (24,10): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] int x) => x; + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(24, 10), + // (26,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] public event Action Event; + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(26, 6), + // (27,6): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] public event Action Event2 + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(27, 6), + // (29,10): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] add {} + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(29, 10), + // (30,10): error CS9331: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + // [MetadataUpdateDeleted] remove {} + Diagnostic(ErrorCode.ERR_AttributeCannotBeAppliedManually, "MetadataUpdateDeleted").WithArguments("System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute").WithLocation(30, 10)); + } + #endregion } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs index ad1e6be9648..bb6dc5aecab 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs @@ -23,6 +23,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; using Xunit; @@ -4441,5 +4442,203 @@ record B(int I) : A(I); var diagnostics = await compWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, filterSpan: null, CancellationToken.None); diagnostics.Verify(Diagnostic("ID0001", "B").WithLocation(1, 8)); } + + private sealed class OptionsOverrideDiagnosticAnalyzer(AnalyzerConfigOptionsProvider customOptions) : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor s_descriptor = new DiagnosticDescriptor( + id: "ID0001", + title: "Title", + messageFormat: "Message", + category: "Category", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private readonly AnalyzerConfigOptionsProvider _customOptions = customOptions; + + public override ImmutableArray SupportedDiagnostics => [s_descriptor]; + + public bool RegisterAdditionalFileActionInvoked { get; private set; } + public bool RegisterCodeBlockActionInvoked { get; private set; } + public bool RegisterCodeBlockStartActionInvoked { get; private set; } + public bool RegisterCompilationActionInvoked { get; private set; } + public bool RegisterOperationActionInvoked { get; private set; } + public bool RegisterOperationBlockActionInvoked { get; private set; } + public bool RegisterSemanticModelActionInvoked { get; private set; } + public bool RegisterSymbolActionInvoked { get; private set; } + public bool RegisterSyntaxNodeActionInvoked { get; private set; } + public bool RegisterSyntaxTreeActionInvoked { get; private set; } + + public bool RegisterOperationBlockStartActionInvoked { get; private set; } + public bool RegisterOperationBlockEndActionInvoked { get; private set; } + public bool RegisterCompilationStartActionInvoked { get; private set; } + public bool RegisterCompilationEndActionInvoked { get; private set; } + public bool RegisterSymbolStartActionInvoked { get; private set; } + public bool RegisterSymbolEndActionInvoked { get; private set; } + + public AnalyzerOptions SeenOptions; + + private void AssertSame(AnalyzerOptions options) + { + // First, assert that the options provider we see is the custom one the test sets. + Assert.Same(options.AnalyzerConfigOptionsProvider, _customOptions); + + if (SeenOptions is null) + SeenOptions = options; + + // Also ensure that the compiler actually passes the same AnalyzerOptions wrapper around + // the options provider. That ensures we're not accidentally creating new instances unnecessarily. + Assert.Same(SeenOptions, options); + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterAdditionalFileAction(context => { AssertSame(context.Options); RegisterAdditionalFileActionInvoked = true; }); + context.RegisterCodeBlockAction(context => { AssertSame(context.Options); RegisterCodeBlockActionInvoked = true; }); + context.RegisterCodeBlockStartAction(context => { AssertSame(context.Options); RegisterCodeBlockStartActionInvoked = true; }); + context.RegisterCompilationAction(context => { AssertSame(context.Options); RegisterCompilationActionInvoked = true; }); + context.RegisterOperationAction(context => { AssertSame(context.Options); RegisterOperationActionInvoked = true; }, OperationKind.Block); + context.RegisterOperationBlockAction(context => { AssertSame(context.Options); RegisterOperationBlockActionInvoked = true; }); + context.RegisterSemanticModelAction(context => { AssertSame(context.Options); RegisterSemanticModelActionInvoked = true; }); + context.RegisterSymbolAction(context => { AssertSame(context.Options); RegisterSymbolActionInvoked = true; }, SymbolKind.NamedType); + context.RegisterSyntaxNodeAction(context => { AssertSame(context.Options); RegisterSyntaxNodeActionInvoked = true; }, SyntaxKind.ClassDeclaration); + context.RegisterSyntaxTreeAction(context => { AssertSame(context.Options); RegisterSyntaxTreeActionInvoked = true; }); + + context.RegisterOperationBlockStartAction(context => + { + AssertSame(context.Options); + RegisterOperationBlockStartActionInvoked = true; + context.RegisterOperationBlockEndAction(context => + { + AssertSame(context.Options); + RegisterOperationBlockEndActionInvoked = true; + }); + }); + + context.RegisterCompilationStartAction(context => + { + AssertSame(context.Options); + RegisterCompilationStartActionInvoked = true; + context.RegisterCompilationEndAction(context => + { + AssertSame(context.Options); + RegisterCompilationEndActionInvoked = true; + }); + }); + context.RegisterSymbolStartAction(context => + { + AssertSame(context.Options); + RegisterSymbolStartActionInvoked = true; + context.RegisterSymbolEndAction(context => + { + AssertSame(context.Options); + RegisterSymbolEndActionInvoked = true; + }); + }, SymbolKind.NamedType); + } + + public void AssertAllCallbacksInvoked() + { + Assert.NotNull(SeenOptions); + + Assert.True(RegisterAdditionalFileActionInvoked); + + Assert.True(RegisterAdditionalFileActionInvoked); + Assert.True(RegisterCodeBlockActionInvoked); + Assert.True(RegisterCodeBlockStartActionInvoked); + Assert.True(RegisterCompilationActionInvoked); + Assert.True(RegisterOperationActionInvoked); + Assert.True(RegisterOperationBlockActionInvoked); + Assert.True(RegisterSemanticModelActionInvoked); + Assert.True(RegisterSymbolActionInvoked); + Assert.True(RegisterSyntaxNodeActionInvoked); + Assert.True(RegisterSyntaxTreeActionInvoked); + + Assert.True(RegisterOperationBlockStartActionInvoked); + Assert.True(RegisterOperationBlockEndActionInvoked); + Assert.True(RegisterCompilationStartActionInvoked); + Assert.True(RegisterCompilationEndActionInvoked); + Assert.True(RegisterSymbolStartActionInvoked); + Assert.True(RegisterSymbolEndActionInvoked); + } + } + + [Fact] + public async Task TestAnalyzerSpecificOptionsFactory() + { + // lang=C#-Test + string source = """ + class C + { + void M() + { + int x = 0; + } + } + """; + + var tree = CSharpSyntaxTree.ParseText(source); + var compilation = CreateCompilationWithCSharp(new[] { tree, CSharpSyntaxTree.ParseText(IsExternalInitTypeDefinition) }); + compilation.VerifyDiagnostics( + // (5,13): warning CS0219: The variable 'x' is assigned but its value is never used + // int x = 0; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(5, 13)); + + var additionalText = new InMemoryAdditionalText("path", "content"); + + // Ensure that the analyzer only sees the custom options passed to the callbacks, and never the shared options. + var sharedOptions = new AnalyzerOptions([additionalText]); + + // Test1. Just a single analyzer. Ensure all callbacks get the custom options. + { + var customOptions = new CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary.Empty, + new DictionaryAnalyzerConfigOptions( + ImmutableDictionary.Empty)); + Assert.NotSame(sharedOptions, customOptions); + + var analyzer = new OptionsOverrideDiagnosticAnalyzer(customOptions); + + var compWithAnalyzers = new CompilationWithAnalyzers( + compilation, + [analyzer], + new CompilationWithAnalyzersOptions( + sharedOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false, reportSuppressedDiagnostics: false, analyzerExceptionFilter: null, + _ => customOptions)); + + var diagnostics = await compWithAnalyzers.GetAllDiagnosticsAsync(); + Assert.Single(diagnostics); + + analyzer.AssertAllCallbacksInvoked(); + } + + // Test2. Two analyzers. Ensure both gets the custom options across all callbacks. + // Also, ensure that across the analyzers we're getting the exact same AnalyzerOptions instance. + { + var customOptions = new CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary.Empty, + new DictionaryAnalyzerConfigOptions( + ImmutableDictionary.Empty)); + Assert.NotSame(sharedOptions, customOptions); + + var analyzer1 = new OptionsOverrideDiagnosticAnalyzer(customOptions); + var analyzer2 = new OptionsOverrideDiagnosticAnalyzer(customOptions); + + var compWithAnalyzers = new CompilationWithAnalyzers( + compilation, + [analyzer1, analyzer2], + new CompilationWithAnalyzersOptions( + sharedOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false, reportSuppressedDiagnostics: false, analyzerExceptionFilter: null, + _ => customOptions)); + + var diagnostics = await compWithAnalyzers.GetAllDiagnosticsAsync(); + Assert.Single(diagnostics); + + analyzer1.AssertAllCallbacksInvoked(); + analyzer2.AssertAllCallbacksInvoked(); + + // Both analyzers should get the exact same AnalyzerOptions instance since they used the same customOptions. + Assert.Same(analyzer1.SeenOptions, analyzer2.SeenOptions); + } + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index 148ea07159a..cbd54a63f5f 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -593,6 +593,130 @@ class C Diagnostic(ErrorCode.ERR_StaticAnonymousFunctionCannotCaptureThis, "field").WithLocation(4, 39)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78932")] + public void Lambda_03() + { + var source = """ + #nullable enable + using System.Collections.Generic; + using System; + + public class DemoCscBreaks + { + public List WillBreak => MethodReturningLambda(() => field ?? new List()); + + private T MethodReturningLambda(Func thisGet) => thisGet(); + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var fieldExpression = tree.GetRoot().DescendantNodes().OfType().Single(); + var fieldType = model.GetTypeInfo(fieldExpression); + AssertEx.Equal("System.Collections.Generic.List?", fieldType.Type.ToTestDisplayString(includeNonNullable: true)); + Assert.Equal(CodeAnalysis.NullableAnnotation.Annotated, fieldType.Type.NullableAnnotation); + + // Note that the member symbol does not expose the inferred nullable annotation via 'FieldSymbol.TypeWithAnnotations'. + var fieldSymbol = comp.GetMember("DemoCscBreaks.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, fieldSymbol.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, ((SynthesizedBackingFieldSymbol)fieldSymbol).GetInferredNullableAnnotation()); + + var publicFieldSymbol = fieldSymbol.GetPublicSymbol(); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, publicFieldSymbol.NullableAnnotation); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78932")] + public void Lambda_04() + { + var source = """ + #nullable enable + using System.Collections.Generic; + using System; + + public class Program + { + public List Prop + { + get => M(() => field); + set => M(() => field = value ?? new List()); + } + + private T M(Func fn) => fn(); + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,25): warning CS9264: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or safely handling the case where 'field' is null in the 'get' accessor. + // public List Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(7, 25)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var fieldExpressions = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, fieldExpressions.Length); + verify(fieldExpressions[0]); + // https://github.com/dotnet/roslyn/issues/77215: a setter should have a maybe-null initial state for the 'field'. + verify(fieldExpressions[1]); + + void verify(FieldExpressionSyntax fieldExpression) + { + var fieldType = model.GetTypeInfo(fieldExpression); + AssertEx.Equal("System.Collections.Generic.List!", fieldType.Type.ToTestDisplayString(includeNonNullable: true)); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, fieldType.Type.NullableAnnotation); + + var fieldSymbol = comp.GetMember("Program.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, fieldSymbol.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, ((SynthesizedBackingFieldSymbol)fieldSymbol).GetInferredNullableAnnotation()); + + var publicFieldSymbol = fieldSymbol.GetPublicSymbol(); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, publicFieldSymbol.NullableAnnotation); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78932")] + public void Lambda_05() + { + var source = """ + #nullable enable + using System.Collections.Generic; + using System; + + public class Program + { + public List Prop => M(() => (List)[field[0].ToString()]); + + private T M(Func fn) => fn(); + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,26): warning CS9264: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or safely handling the case where 'field' is null in the 'get' accessor. + // public List Prop => M(() => (List)[field[0].ToString()]); + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(7, 26), + // (7,58): warning CS8602: Dereference of a possibly null reference. + // public List Prop => M(() => (List)[field[0].ToString()]); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field[0]").WithLocation(7, 58)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var fieldExpression = tree.GetRoot().DescendantNodes().OfType().Single(); + var fieldType = model.GetTypeInfo(fieldExpression); + AssertEx.Equal("System.Collections.Generic.List!", fieldType.Type.ToTestDisplayString(includeNonNullable: true)); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, fieldType.Type.NullableAnnotation); + + var fieldSymbol = comp.GetMember("Program.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, fieldSymbol.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, ((SynthesizedBackingFieldSymbol)fieldSymbol).GetInferredNullableAnnotation()); + + var publicFieldSymbol = fieldSymbol.GetPublicSymbol(); + Assert.Equal(CodeAnalysis.NullableAnnotation.NotAnnotated, publicFieldSymbol.NullableAnnotation); + } + [Fact] public void LocalFunction_01() { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj index 801f88a7e61..be883e4c060 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj @@ -29,6 +29,7 @@ + diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index 3edeaabec13..476539e71fd 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -11994,6 +11994,404 @@ static void Main() Verification.Skipped).VerifyDiagnostics(); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/80041")] + public void Foreach_Span_01() + { + var src = @" +class C +{ + static void Test(System.ReadOnlySpan collection) + { + foreach(var c in collection) + { + } + } +} +"; + + var core = +@" +namespace System +{ + public class Object { } + public abstract class ValueType { } + public class String { } + public class Type { } + public struct Void { } + public struct Boolean { } + public struct Int32 { } + public struct Char { } + public struct Enum { } + public class Attribute { } + public class AttributeUsageAttribute : Attribute + { + public AttributeUsageAttribute(AttributeTargets t) { } + public bool AllowMultiple { get; set; } + public bool Inherited { get; set; } + } + public enum AttributeTargets { } + + public interface IDisposable + { + void Dispose(); + } +} +namespace System.Collections +{ + public interface IEnumerator + { + bool MoveNext(); + object Current { get; } + } + public interface IEnumerable + { + IEnumerator GetEnumerator(); + } +} +namespace System.Collections.Generic +{ + public interface IEnumerator : IDisposable, IEnumerator + { + new T Current { get; } + } + public interface IEnumerable : IEnumerable + { + new IEnumerator GetEnumerator(); + } +} + +namespace System.Runtime.InteropServices +{ + public class InAttribute {} +} + +namespace System.Reflection +{ + public class DefaultMemberAttribute : Attribute + { + public DefaultMemberAttribute(string name) { } + } +} +"; + + var expectedIL = +@" +{ + // Code size 30 (0x1e) + .maxstack 2 + .locals init (System.ReadOnlySpan V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: br.s IL_0013 + IL_0006: ldloca.s V_0 + IL_0008: ldloc.1 + IL_0009: call ""ref readonly char System.ReadOnlySpan.this[int].get"" + IL_000e: pop + IL_000f: ldloc.1 + IL_0010: ldc.i4.1 + IL_0011: add + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_0 + IL_0016: call ""int System.ReadOnlySpan.Length.get"" + IL_001b: blt.s IL_0006 + IL_001d: ret +} +"; + + var span1 = @" +namespace System +{ + public readonly ref struct ReadOnlySpan + { + private static T _t; + public int Length => 0; + public ref readonly T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator + { + public ref readonly T Current => ref _t; + + public bool MoveNext() => false; + } + } +} +"; + var span = CreateEmptyCompilation([core, span1]).EmitToImageReference(); + + var comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span2 = @" +namespace System +{ + public readonly ref struct ReadOnlySpan + { + private static T _t; + public int Length => 0; + public ref readonly T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator : System.Collections.Generic.IEnumerator + { + public ref readonly T Current => ref _t; + + public bool MoveNext() => false; + + T System.Collections.Generic.IEnumerator.Current => default; + object System.Collections.IEnumerator.Current => default; + void System.IDisposable.Dispose() { } + } + } +} +"; + span = CreateEmptyCompilation([core, span2]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span3 = @" +namespace System +{ + public readonly ref struct ReadOnlySpan : System.Collections.Generic.IEnumerable + { + private static T _t; + public int Length => 0; + public ref readonly T this[int index] => ref _t; + + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => default; + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => default; + } +} +"; + span = CreateEmptyCompilation([core, span3]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/80041")] + public void Foreach_Span_02() + { + var src = @" +class C +{ + static void Test(System.Span collection) + { + foreach(var c in collection) + { + } + } +} +"; + + var core = +@" +namespace System +{ + public class Object { } + public abstract class ValueType { } + public class String { } + public class Type { } + public struct Void { } + public struct Boolean { } + public struct Int32 { } + public struct Char { } + public struct Enum { } + public class Attribute { } + public class AttributeUsageAttribute : Attribute + { + public AttributeUsageAttribute(AttributeTargets t) { } + public bool AllowMultiple { get; set; } + public bool Inherited { get; set; } + } + public enum AttributeTargets { } + + public interface IDisposable + { + void Dispose(); + } +} +namespace System.Collections +{ + public interface IEnumerator + { + bool MoveNext(); + object Current { get; } + } + public interface IEnumerable + { + IEnumerator GetEnumerator(); + } +} +namespace System.Collections.Generic +{ + public interface IEnumerator : IDisposable, IEnumerator + { + new T Current { get; } + } + public interface IEnumerable : IEnumerable + { + new IEnumerator GetEnumerator(); + } +} + +namespace System.Runtime.InteropServices +{ + public class InAttribute {} +} + +namespace System.Reflection +{ + public class DefaultMemberAttribute : Attribute + { + public DefaultMemberAttribute(string name) { } + } +} +"; + + var expectedIL = + @" +{ + // Code size 30 (0x1e) + .maxstack 2 + .locals init (System.Span V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: br.s IL_0013 + IL_0006: ldloca.s V_0 + IL_0008: ldloc.1 + IL_0009: call ""ref char System.Span.this[int].get"" + IL_000e: pop + IL_000f: ldloc.1 + IL_0010: ldc.i4.1 + IL_0011: add + IL_0012: stloc.1 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_0 + IL_0016: call ""int System.Span.Length.get"" + IL_001b: blt.s IL_0006 + IL_001d: ret +} +"; + + var span1 = @" +namespace System +{ + public readonly ref struct Span + { + private static T _t; + public int Length => 0; + public ref T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator + { + public ref T Current => ref _t; + + public bool MoveNext() => false; + } + } +} +"; + var span = CreateEmptyCompilation([core, span1]).EmitToImageReference(); + + var comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span2 = @" +namespace System +{ + public readonly ref struct Span + { + private static T _t; + public int Length => 0; + public ref T this[int index] => ref _t; + + public Enumerator GetEnumerator() => default; + + public ref struct Enumerator : System.Collections.Generic.IEnumerator + { + public ref readonly T Current => ref _t; + + public bool MoveNext() => false; + + T System.Collections.Generic.IEnumerator.Current => default; + object System.Collections.IEnumerator.Current => default; + void System.IDisposable.Dispose() { } + } + } +} +"; + span = CreateEmptyCompilation([core, span2]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + var span3 = @" +namespace System +{ + public readonly ref struct Span : System.Collections.Generic.IEnumerable + { + private static T _t; + public int Length => 0; + public ref T this[int index] => ref _t; + + System.Collections.Generic.IEnumerator System.Collections.Generic.IEnumerable.GetEnumerator() => default; + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => default; + } +} +"; + span = CreateEmptyCompilation([core, span3]).EmitToImageReference(); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateEmptyCompilation(src, references: [span], options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + + comp = CreateCompilation(src, targetFramework: TargetFramework.Net100, options: TestOptions.ReleaseDll, parseOptions: TestOptions.Regular11); + CompileAndVerify(comp, verify: Verification.Skipped).VerifyDiagnostics().VerifyIL("C.Test", expectedIL); + } + [Fact] public void AwaitUsing_01() { @@ -22073,8 +22471,6 @@ public class Unsafe public void AsyncParameter() { var src = @" -#pragma warning disable CS1998 // This async method lacks 'await' operators and will run synchronously. - public class Helper { static async void Test1(T x) @@ -22094,12 +22490,12 @@ ref struct S var comp = CreateCompilation(src, targetFramework: s_targetFrameworkSupportingByRefLikeGenerics); comp.VerifyDiagnostics( - // (6,34): error CS4012: Parameters of type 'T' cannot be declared in async methods or async lambda expressions. + // (4,34): error CS4012: Parameters of type 'T' cannot be declared in async methods or async lambda expressions. // static async void Test1(T x) - Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "x").WithArguments("T").WithLocation(6, 34), - // (11,31): error CS4012: Parameters of type 'S' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "x").WithArguments("T").WithLocation(4, 34), + // (9,31): error CS4012: Parameters of type 'S' cannot be declared in async methods or async lambda expressions. // static async void Test2(S y) - Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "y").WithArguments("S").WithLocation(11, 31) + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "y").WithArguments("S").WithLocation(9, 31) ); } @@ -29118,8 +29514,6 @@ public void Iterator_04() { var source = @" -#pragma warning disable CS1998 // This async method lacks 'await' operators - using System.Collections.Generic; static class CSharpCompilerCrash @@ -29135,9 +29529,9 @@ static async IAsyncEnumerable B() "; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); comp.VerifyDiagnostics( - // (8,47): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs + // (6,47): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs // static async IAsyncEnumerable B() - Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(8, 47) + Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(6, 47) ); } @@ -29148,8 +29542,6 @@ public void Iterator_05() { var source = @" -#pragma warning disable CS1998 // This async method lacks 'await' operators - using System.Collections.Generic; static class CSharpCompilerCrash @@ -29165,9 +29557,9 @@ static async IAsyncEnumerator B() "; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); comp.VerifyDiagnostics( - // (8,47): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs + // (6,47): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs // static async IAsyncEnumerator B() - Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(8, 47) + Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(6, 47) ); } @@ -29178,8 +29570,6 @@ public void Iterator_06() { var source = @" -#pragma warning disable CS1998 // This async method lacks 'await' operators - using System.Collections.Generic; static class CSharpCompilerCrash @@ -29193,9 +29583,9 @@ static async IAsyncEnumerator B() where T : allows ref struct "; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); comp.VerifyDiagnostics( - // (8,38): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs + // (6,38): error CS9266: Element type of an iterator may not be a ref struct or a type parameter allowing ref structs // static async IAsyncEnumerator B() where T : allows ref struct - Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(8, 38) + Diagnostic(ErrorCode.ERR_IteratorRefLikeElementType, "B").WithLocation(6, 38) ); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index 548d1632948..9ff7a042808 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -70,6 +70,8 @@ private static void AssertExtensionDeclaration(INamedTypeSymbol symbol) // Verify things that are common for all extension types Assert.Equal(TypeKind.Extension, symbol.TypeKind); Assert.True(symbol.IsExtension); + Assert.False(string.IsNullOrEmpty(symbol.ExtensionGroupingName)); + Assert.False(string.IsNullOrEmpty(symbol.ExtensionMarkerName)); Assert.Null(symbol.BaseType); Assert.Empty(symbol.Interfaces); Assert.Empty(symbol.AllInterfaces); @@ -192,7 +194,10 @@ .maxstack 8 AssertEx.Equal("$C43E2675C7BBF9284AF22FB8A9BF0280", symbol.MetadataName); var member = symbol.ContainingType.GetMembers().Single(); - AssertEx.Equal("Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280", member.ToTestDisplayString()); + AssertEx.Equal("Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.$C43E2675C7BBF9284AF22FB8A9BF0280", member.ToTestDisplayString()); + var underlying = (SourceNamedTypeSymbol)((Symbols.PublicModel.NamedTypeSymbol)member).UnderlyingNamedTypeSymbol; + AssertEx.Equal("extension(System.Object)", underlying.ComputeExtensionGroupingRawName()); + AssertEx.Equal("extension(System.Object)", underlying.ComputeExtensionMarkerRawName()); var format = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); AssertEx.Equal("Extensions.extension(System.Object)", symbol.ToDisplayString(format)); @@ -204,7 +209,7 @@ .maxstack 8 AssertEx.Equal("extension(Object)", symbol.ToDisplayString(format)); format = new SymbolDisplayFormat(compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames); - AssertEx.Equal("$C43E2675C7BBF9284AF22FB8A9BF0280", symbol.ToDisplayString(format)); + AssertEx.Equal("$C43E2675C7BBF9284AF22FB8A9BF0280.$C43E2675C7BBF9284AF22FB8A9BF0280", symbol.ToDisplayString(format)); var comp5 = CreateCompilation(src); comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); @@ -237,7 +242,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -248,10 +253,10 @@ .class nested public auto ansi abstract sealed specialname '$01CE3801593377B4 extends [mscorlib]System.Object { // Methods - .method private hidebysig specialname static + .method private hidebysig specialname static void '$' ( !T '' - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -262,7 +267,7 @@ .maxstack 8 IL_0000: ret } // end of method '$01CE3801593377B4E240F33E20D30D50'::'$' } // end of class $01CE3801593377B4E240F33E20D30D50 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of class $8048A6C8BE30A622530249B904B537EB`1 } // end of class Extensions """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); @@ -285,18 +290,18 @@ .maxstack 8 AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol.MetadataName); var member = symbol.ContainingType.GetMembers().Single(); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", member.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", member.ToTestDisplayString()); var constructed = symbol.Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.True(constructed.IsExtension); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", constructed.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", constructed.ToTestDisplayString()); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", constructed.MetadataName); Assert.NotSame(symbol, constructed); Assert.Same(symbol, constructed.OriginalDefinition); Assert.Same(symbol, constructed.ConstructedFrom); var unbound = symbol.ConstructUnboundGenericType(); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB<>", unbound.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB<>.$01CE3801593377B4E240F33E20D30D50", unbound.ToTestDisplayString()); Assert.True(unbound.IsUnboundGenericType); Assert.NotSame(symbol, unbound); Assert.Same(symbol, unbound.OriginalDefinition); @@ -325,7 +330,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$BCF902721DDD961E5243C324D8379E5C' + .class nested public auto ansi sealed specialname '$BCF902721DDD961E5243C324D8379E5C`1' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -350,7 +355,7 @@ .maxstack 8 IL_0000: ret } // end of method '$B865B3ED3C68CE2EBBC104FFAF3CFF93'::'$' } // end of class $B865B3ED3C68CE2EBBC104FFAF3CFF93 - } // end of class $BCF902721DDD961E5243C324D8379E5C + } // end of class $BCF902721DDD961E5243C324D8379E5C`1 } // end of class Extensions """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); @@ -515,7 +520,7 @@ public static class Extensions var container = symbol.ContainingType; var substitutedExtension = (INamedTypeSymbol)container.Construct(comp.GetSpecialType(SpecialType.System_Int32)).GetMembers().Single(); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", substitutedExtension.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", substitutedExtension.ToTestDisplayString()); Assert.True(substitutedExtension.IsExtension); } @@ -607,7 +612,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -622,10 +627,10 @@ .param type T 01 00 01 00 00 ) // Methods - .method private hidebysig specialname static + .method private hidebysig specialname static void '$' ( !T '' - ) cil managed + ) cil managed { .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( 01 00 01 00 00 @@ -639,7 +644,7 @@ .maxstack 8 IL_0000: ret } // end of method '$C7A07C3975E80DE5DBC93B5392C6C922'::'$' } // end of class $C7A07C3975E80DE5DBC93B5392C6C922 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of class $8048A6C8BE30A622530249B904B537EB`1 } // end of class Extensions """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } @@ -672,7 +677,10 @@ public static class Extensions AssertExtensionDeclaration(symbol); Assert.True(symbol.IsGenericType); var members = symbol.ContainingType.GetMembers(); - AssertEx.SequenceEqual(["Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280", "void Extensions.M()"], members.ToTestDisplayStrings()); + AssertEx.SequenceEqual([ + "Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.$C43E2675C7BBF9284AF22FB8A9BF0280", + "void Extensions.M()" + ], members.ToTestDisplayStrings()); } [Fact] @@ -699,7 +707,7 @@ public void BadContainer_TopLevel() var symbol = model.GetDeclaredSymbol(extension); AssertExtensionDeclaration(symbol); Assert.Null(symbol.ContainingType); - AssertEx.Equal("$C43E2675C7BBF9284AF22FB8A9BF0280", symbol.ToTestDisplayString()); + AssertEx.Equal("$C43E2675C7BBF9284AF22FB8A9BF0280.$C43E2675C7BBF9284AF22FB8A9BF0280", symbol.ToTestDisplayString()); } [Fact] @@ -741,7 +749,10 @@ public static class Extensions2 AssertExtensionDeclaration(nestedExtensionSymbol); AssertEx.Equal("Extensions.Extensions2", nestedExtensionSymbol.ContainingType.ToTestDisplayString()); var members = nestedExtensionSymbol.ContainingType.GetMembers(); - AssertEx.SequenceEqual(["Extensions.Extensions2.$C43E2675C7BBF9284AF22FB8A9BF0280", "void Extensions.Extensions2.M()"], members.ToTestDisplayStrings()); + AssertEx.SequenceEqual([ + "Extensions.Extensions2.$C43E2675C7BBF9284AF22FB8A9BF0280.$C43E2675C7BBF9284AF22FB8A9BF0280", + "void Extensions.Extensions2.M()" + ], members.ToTestDisplayStrings()); } [Fact] @@ -789,8 +800,12 @@ static void Method() var nestedExtensionSymbol = model.GetDeclaredSymbol(nestedExtension); AssertExtensionDeclaration(nestedExtensionSymbol); - AssertEx.Equal("Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280", nestedExtensionSymbol.ContainingType.ToTestDisplayString()); - AssertEx.SequenceEqual(["void Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.Method()", "Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.$34505F560D9EACF86A87F3ED1F85E448"], nestedExtensionSymbol.ContainingType.GetMembers().ToTestDisplayStrings()); + AssertEx.Equal("Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.$C43E2675C7BBF9284AF22FB8A9BF0280", + nestedExtensionSymbol.ContainingType.ToTestDisplayString()); + AssertEx.SequenceEqual([ + "void Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.Method()", + "Extensions.$C43E2675C7BBF9284AF22FB8A9BF0280.$34505F560D9EACF86A87F3ED1F85E448.$34505F560D9EACF86A87F3ED1F85E448" + ], nestedExtensionSymbol.ContainingType.GetMembers().ToTestDisplayStrings()); } [Fact] @@ -929,7 +944,7 @@ public static partial class Extensions var symbol1 = model1.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$D1693D81A12E8DED4ED68FE22D9E856F", symbol1.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", sourceExtension1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", sourceExtension1.ToTestDisplayString()); var tree2 = comp.SyntaxTrees[1]; var extension2 = tree2.GetRoot().DescendantNodes().OfType().Single(); @@ -937,7 +952,7 @@ public static partial class Extensions var symbol2 = model2.GetDeclaredSymbol(extension2); var sourceExtension2 = symbol2.GetSymbol(); AssertEx.Equal("$38DD3033A2145E0D2274BCCB48D8434F", symbol2.MetadataName); - AssertEx.Equal("Extensions.$B6FEF98A1719AAFE96009C5CC65441CB", sourceExtension2.ToTestDisplayString()); + AssertEx.Equal("Extensions.$B6FEF98A1719AAFE96009C5CC65441CB.$38DD3033A2145E0D2274BCCB48D8434F", sourceExtension2.ToTestDisplayString()); } [Fact] @@ -962,12 +977,12 @@ public static partial class Extensions var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); var symbol1 = model.GetDeclaredSymbol(extension1); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol1.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol1.ToTestDisplayString()); var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); var symbol2 = model.GetDeclaredSymbol(extension2); AssertEx.Equal("$0A2F70F0BFFD1BC7F8C8E0A6CD0B0194", symbol2.MetadataName); - AssertEx.Equal("Extensions.$B6FEF98A1719AAFE96009C5CC65441CB", symbol2.ToTestDisplayString()); + AssertEx.Equal("Extensions.$B6FEF98A1719AAFE96009C5CC65441CB.$0A2F70F0BFFD1BC7F8C8E0A6CD0B0194", symbol2.ToTestDisplayString()); } [Fact] @@ -990,13 +1005,13 @@ public static class Extensions var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol1.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol1.ToTestDisplayString()); var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); var symbol2 = model.GetDeclaredSymbol(extension2); var sourceExtension2 = symbol2.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol2.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol2.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol2.ToTestDisplayString()); } [Fact] @@ -1294,7 +1309,7 @@ .maxstack 8 var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$CD29E70E0DCA5BBFCFAC7C2BEF3C5C99", symbol1.MetadataName); - AssertEx.Equal("Extensions.$CD29E70E0DCA5BBFCFAC7C2BEF3C5C99", symbol1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$CD29E70E0DCA5BBFCFAC7C2BEF3C5C99.$CD29E70E0DCA5BBFCFAC7C2BEF3C5C99", symbol1.ToTestDisplayString()); } [Fact] @@ -1471,7 +1486,7 @@ [1] int32 var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$BA41CFE2B5EDAEB8C1B9062F59ED4D69", symbol1.MetadataName); - AssertEx.Equal("IntExt.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", symbol1.ToTestDisplayString()); + AssertEx.Equal("IntExt.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", symbol1.ToTestDisplayString()); } [Fact] @@ -1645,7 +1660,7 @@ [0] int32 var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$BA41CFE2B5EDAEB8C1B9062F59ED4D69", symbol1.MetadataName); - AssertEx.Equal("IntExt.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", symbol1.ToTestDisplayString()); + AssertEx.Equal("IntExt.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", symbol1.ToTestDisplayString()); } [Fact] @@ -1669,13 +1684,13 @@ class C { } var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol1.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol1.ToTestDisplayString()); var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); var symbol2 = model.GetDeclaredSymbol(extension2); var sourceExtension2 = symbol2.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol2.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol2.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol2.ToTestDisplayString()); } [Fact] @@ -1702,13 +1717,13 @@ class C { } var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol1.MetadataName); - AssertEx.Equal("$8048A6C8BE30A622530249B904B537EB", symbol1.ToTestDisplayString()); + AssertEx.Equal("$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol1.ToTestDisplayString()); var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); var symbol2 = model.GetDeclaredSymbol(extension2); var sourceExtension2 = symbol2.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol2.MetadataName); - AssertEx.Equal("$8048A6C8BE30A622530249B904B537EB", symbol2.ToTestDisplayString()); + AssertEx.Equal("$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol2.ToTestDisplayString()); } [Fact] @@ -1731,13 +1746,13 @@ public static class Extensions var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol1.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol1.ToTestDisplayString()); var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); var symbol2 = model.GetDeclaredSymbol(extension2); var sourceExtension2 = symbol2.GetSymbol(); AssertEx.Equal("$38DD3033A2145E0D2274BCCB48D8434F", symbol2.MetadataName); - AssertEx.Equal("Extensions.$B6FEF98A1719AAFE96009C5CC65441CB", symbol2.ToTestDisplayString()); + AssertEx.Equal("Extensions.$B6FEF98A1719AAFE96009C5CC65441CB.$38DD3033A2145E0D2274BCCB48D8434F", symbol2.ToTestDisplayString()); } [Fact] @@ -1759,13 +1774,13 @@ public static class Extensions var symbol1 = model.GetDeclaredSymbol(extension1); var sourceExtension1 = symbol1.GetSymbol(); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", symbol1.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol1.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", symbol1.ToTestDisplayString()); var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); var symbol2 = model.GetDeclaredSymbol(extension2); var sourceExtension2 = symbol2.GetSymbol(); AssertEx.Equal("$0F0A7F439039332917C923D7DF48FA4C", symbol2.MetadataName); - AssertEx.Equal("Extensions.$BCF902721DDD961E5243C324D8379E5C", symbol2.ToTestDisplayString()); + AssertEx.Equal("Extensions.$BCF902721DDD961E5243C324D8379E5C.$0F0A7F439039332917C923D7DF48FA4C", symbol2.ToTestDisplayString()); } [Fact] @@ -1798,7 +1813,7 @@ class C { } var symbol = model.GetDeclaredSymbol(extension); var sourceExtension = symbol.GetSymbol(); AssertEx.Equal("$9B08A69343790083B512FC2D1C4929FC", symbol.MetadataName); - AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB", symbol.ToTestDisplayString()); + AssertEx.Equal("Extensions.$8048A6C8BE30A622530249B904B537EB.$9B08A69343790083B512FC2D1C4929FC", symbol.ToTestDisplayString()); } [Fact] @@ -4811,6 +4826,12 @@ public static void M2(this int* i) { } // (8,32): error CS1103: The receiver parameter of an extension cannot be of type 'int*' // public static void M2(this int* i) { } Diagnostic(ErrorCode.ERR_BadTypeforThis, "int*").WithArguments("int*").WithLocation(8, 32)); + + NamedTypeSymbol e = comp.GlobalNamespace.GetTypeMember("E"); + Assert.IsType(e); + Assert.False(e.IsExtension); + Assert.Null(e.ExtensionGroupingName); + Assert.Null(e.ExtensionMarkerName); } [Fact] @@ -6764,7 +6785,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9'<$T0> + .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -6775,10 +6796,10 @@ .class nested public auto ansi abstract sealed specialname '$D884D1E13988E838 extends [mscorlib]System.Object { // Methods - .method public hidebysig specialname static + .method public hidebysig specialname static void '$' ( class C`1 o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -6790,11 +6811,11 @@ .maxstack 8 } // end of method '$D884D1E13988E83801B7574694E1C2C5'::'$' } // end of class $D884D1E13988E83801B7574694E1C2C5 // Methods - .method public hidebysig + .method public hidebysig instance string M ( !$T0 t, !!U u - ) cil managed + ) cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 44 38 38 34 44 31 45 31 33 @@ -6806,15 +6827,15 @@ 34 45 31 43 32 43 35 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9'::M - } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9 + } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'::M + } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9`1 // Methods - .method public hidebysig static + .method public hidebysig static string M ( class C`1 o, !!T t, !!U u - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -7019,7 +7040,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9'<$T0> + .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -7061,8 +7082,8 @@ 34 45 31 43 32 43 35 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9'::M - } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9 + } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'::M + } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9`1 .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0`2' extends [mscorlib]System.ValueType { @@ -7308,7 +7329,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9'<$T0> + .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -7350,8 +7371,8 @@ 34 45 31 43 32 43 35 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9'::M - } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9 + } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'::M + } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9`1 .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0`2' extends [mscorlib]System.Object { @@ -7581,7 +7602,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9'<$T0> + .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -7623,8 +7644,8 @@ 34 45 31 43 32 43 35 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9'::M - } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9 + } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'::M + } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9`1 .class nested private auto ansi sealed beforefieldinit 'd__1`2' extends [mscorlib]System.Object implements class [mscorlib]System.Collections.Generic.IEnumerable`1, @@ -7984,7 +8005,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9'<$T0> + .class nested public auto ansi sealed specialname '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -8026,8 +8047,8 @@ 34 45 31 43 32 43 35 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9'::M - } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9 + } // end of method '$4A1E373BE5A70EE56E2FA5F469AC30F9`1'::M + } // end of class $4A1E373BE5A70EE56E2FA5F469AC30F9`1 .class nested private auto ansi sealed beforefieldinit 'd__1`2' extends [mscorlib]System.ValueType implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine @@ -10811,7 +10832,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -10850,8 +10871,8 @@ 45 36 32 37 31 30 32 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$8048A6C8BE30A622530249B904B537EB'::M2 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of method '$8048A6C8BE30A622530249B904B537EB`1'::M2 + } // end of class $8048A6C8BE30A622530249B904B537EB`1 .class nested private auto ansi abstract sealed beforefieldinit 'O__1_0`3' extends [mscorlib]System.Object { @@ -10976,7 +10997,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -11015,8 +11036,8 @@ 45 36 32 37 31 30 32 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$8048A6C8BE30A622530249B904B537EB'::M2 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of method '$8048A6C8BE30A622530249B904B537EB`1'::M2 + } // end of class $8048A6C8BE30A622530249B904B537EB`1 .class nested private auto ansi abstract sealed beforefieldinit '<>O__1_0`1' extends [mscorlib]System.Object { @@ -11291,7 +11312,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -11302,10 +11323,10 @@ .class nested public auto ansi abstract sealed specialname '$D3EAC011D93395A3 extends [mscorlib]System.Object { // Methods - .method private hidebysig specialname static + .method private hidebysig specialname static void '$' ( !T o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -11317,8 +11338,8 @@ .maxstack 8 } // end of method '$D3EAC011D93395A3E50DF069CE627102'::'$' } // end of class $D3EAC011D93395A3E50DF069CE627102 // Methods - .method private hidebysig - instance class [mscorlib]System.Action M2 () cil managed + .method private hidebysig + instance class [mscorlib]System.Action M2 () cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 44 33 45 41 43 30 31 31 44 @@ -11330,8 +11351,8 @@ 45 36 32 37 31 30 32 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$8048A6C8BE30A622530249B904B537EB'::M2 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of method '$8048A6C8BE30A622530249B904B537EB`1'::M2 + } // end of class $8048A6C8BE30A622530249B904B537EB`1 .class nested private auto ansi abstract sealed beforefieldinit '<>O__1_0`1' extends [mscorlib]System.Object { @@ -11342,10 +11363,10 @@ 01 00 00 00 .field public static class [mscorlib]System.Action '<0>__local' } // end of class <>O__1_0`1 // Methods - .method private hidebysig static + .method private hidebysig static class [mscorlib]System.Action M2 ( !!T o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -11364,8 +11385,8 @@ .maxstack 8 IL_0016: stsfld class [mscorlib]System.Action class Extensions/'<>O__1_0`1'::'<0>__local' IL_001b: ret } // end of method Extensions::M2 - .method assembly hidebysig static - void 'g__local|1_0' () cil managed + .method assembly hidebysig static + void 'g__local|1_0' () cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -11610,7 +11631,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -11621,10 +11642,10 @@ .class nested public auto ansi abstract sealed specialname '$D3EAC011D93395A3 extends [mscorlib]System.Object { // Methods - .method public hidebysig specialname static + .method public hidebysig specialname static void '$' ( !T o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -11636,8 +11657,8 @@ .maxstack 8 } // end of method '$D3EAC011D93395A3E50DF069CE627102'::'$' } // end of class $D3EAC011D93395A3E50DF069CE627102 // Methods - .method public hidebysig - instance void M2 () cil managed + .method public hidebysig + instance void M2 () cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 44 33 45 41 43 30 31 31 44 @@ -11649,8 +11670,8 @@ 45 36 32 37 31 30 32 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$8048A6C8BE30A622530249B904B537EB'::M2 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of method '$8048A6C8BE30A622530249B904B537EB`1'::M2 + } // end of class $8048A6C8BE30A622530249B904B537EB`1 .class nested private auto ansi abstract sealed beforefieldinit '<>o__0|1`3' extends [mscorlib]System.Object { @@ -11661,10 +11682,10 @@ 01 00 00 00 .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1> '<>p__0' } // end of class <>o__0|1`3 // Methods - .method public hidebysig static + .method public hidebysig static void M2 ( !!T o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -11688,13 +11709,13 @@ .locals init ( IL_001a: call void Extensions::'g__local|1_0'(object, !!0, !!1, !!2) IL_001f: ret } // end of method Extensions::M2 - .method assembly hidebysig static + .method assembly hidebysig static void 'g__local|1_0' ( object d, !!T t, !!U u, !!V v - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -11817,7 +11838,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -11828,10 +11849,10 @@ .class nested public auto ansi abstract sealed specialname '$D3EAC011D93395A3 extends [mscorlib]System.Object { // Methods - .method public hidebysig specialname static + .method public hidebysig specialname static void '$' ( !T o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -11843,8 +11864,8 @@ .maxstack 8 } // end of method '$D3EAC011D93395A3E50DF069CE627102'::'$' } // end of class $D3EAC011D93395A3E50DF069CE627102 // Methods - .method public hidebysig - instance void M2 () cil managed + .method public hidebysig + instance void M2 () cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 44 33 45 41 43 30 31 31 44 @@ -11856,8 +11877,8 @@ 45 36 32 37 31 30 32 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$8048A6C8BE30A622530249B904B537EB'::M2 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of method '$8048A6C8BE30A622530249B904B537EB`1'::M2 + } // end of class $8048A6C8BE30A622530249B904B537EB`1 .class nested private auto ansi abstract sealed beforefieldinit '<>o__1`1' extends [mscorlib]System.Object { @@ -11868,10 +11889,10 @@ 01 00 00 00 .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1> '<>p__0' } // end of class <>o__1`1 // Methods - .method public hidebysig static + .method public hidebysig static void M2 ( !!T o - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -11881,11 +11902,11 @@ 01 00 00 00 .maxstack 8 IL_0000: ret } // end of method Extensions::M2 - .method assembly hidebysig static + .method assembly hidebysig static void 'g__local|1_0' ( object d, !!T t - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -22076,7 +22097,7 @@ .locals init (int& V_0) .VerifyDiagnostics(); } - [Theory(Skip = "https://github.com/dotnet/roslyn/issues/78433"), WorkItem("https://github.com/dotnet/roslyn/issues/78137")] + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78433"), WorkItem("https://github.com/dotnet/roslyn/issues/78137")] [CombinatorialData] public void InterpolationHandler_ReceiverParameter_ByRef_WithConstantReceiver(bool useMetadataRef) { @@ -22112,7 +22133,12 @@ public void M([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument """; var expectedDiagnostic = new DiagnosticDescription[] { - // To be filled in when test is unskipped + // (1,1): error CS1510: A ref or out value must be an assignable variable + // 1.M($""); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "1").WithLocation(1, 1), + // (2,5): error CS1620: Argument 1 must be passed with the 'ref' keyword + // E.M(3, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "3").WithArguments("1", "ref").WithLocation(2, 5) }; CreateCompilation([exeSource, src], targetFramework: TargetFramework.Net90).VerifyDiagnostics(expectedDiagnostic); @@ -33209,7 +33235,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$373395272A45479DE48E8BB1CCB2C42B'<$T0, $T1> + .class nested public auto ansi sealed specialname '$373395272A45479DE48E8BB1CCB2C42B`2'<$T0, $T1> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -33220,10 +33246,10 @@ 01 00 00 00 extends [mscorlib]System.Object { // Methods - .method assembly hidebysig specialname static + .method assembly hidebysig specialname static void '$' ( class C`2 c - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -33235,8 +33261,8 @@ .maxstack 8 } // end of method '$80D5112A03B26C94C628316C4DA793B2'::'$' } // end of class $80D5112A03B26C94C628316C4DA793B2 // Methods - .method assembly hidebysig - instance void M1 () cil managed + .method assembly hidebysig + instance void M1 () cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 38 30 44 35 31 31 32 41 30 @@ -33248,9 +33274,9 @@ 44 41 37 39 33 42 32 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$373395272A45479DE48E8BB1CCB2C42B'::M1 - } // end of class $373395272A45479DE48E8BB1CCB2C42B - .class nested public auto ansi sealed specialname '$6D4255504AB27A230E5AB4858D9E46EB'<$T0, $T1> + } // end of method '$373395272A45479DE48E8BB1CCB2C42B`2'::M1 + } // end of class $373395272A45479DE48E8BB1CCB2C42B`2 + .class nested public auto ansi sealed specialname '$6D4255504AB27A230E5AB4858D9E46EB`2'<$T0, $T1> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -33261,10 +33287,10 @@ 01 00 00 00 extends [mscorlib]System.Object { // Methods - .method assembly hidebysig specialname static + .method assembly hidebysig specialname static void '$' ( class C`2 c - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -33276,8 +33302,8 @@ .maxstack 8 } // end of method '$2237E852D2E9F48E0CC6BF2FD528DA2A'::'$' } // end of class $2237E852D2E9F48E0CC6BF2FD528DA2A // Methods - .method assembly hidebysig - instance void M1 () cil managed + .method assembly hidebysig + instance void M1 () cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 32 32 33 37 45 38 35 32 44 @@ -33289,13 +33315,13 @@ 35 32 38 44 41 32 41 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$6D4255504AB27A230E5AB4858D9E46EB'::M1 - } // end of class $6D4255504AB27A230E5AB4858D9E46EB + } // end of method '$6D4255504AB27A230E5AB4858D9E46EB`2'::M1 + } // end of class $6D4255504AB27A230E5AB4858D9E46EB`2 // Methods - .method assembly hidebysig static + .method assembly hidebysig static void M1 ( class C`2 c - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -33305,10 +33331,10 @@ 01 00 00 00 .maxstack 8 IL_0000: ret } // end of method Extensions1::M1 - .method assembly hidebysig static + .method assembly hidebysig static void M1 ( class C`2 c - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -41839,13 +41865,8 @@ unsafe static class E comp.VerifyEmitDiagnostics( // (3,15): error CS1103: The receiver parameter of an extension cannot be of type 'int*' // extension(int* i) - Diagnostic(ErrorCode.ERR_BadTypeforThis, "int*").WithArguments("int*").WithLocation(3, 15), - // (5,34): 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. - // public static async void M() => throw null; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(5, 34), - // (6,27): 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. - // public async void M2() => throw null; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M2").WithLocation(6, 27)); + Diagnostic(ErrorCode.ERR_BadTypeforThis, "int*").WithArguments("int*").WithLocation(3, 15) + ); } [Fact] @@ -49968,9 +49989,9 @@ static class E """, e.GetDocumentationCommentXml()); var extension = e.GetTypeMembers().Single(); - AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); AssertEx.Equal(""" - + Summary for extension block Description for T Description for t @@ -49980,7 +50001,7 @@ static class E var mSkeleton = extension.GetMember("M"); AssertEx.Equal(""" - + Summary for M Description for U Description for u @@ -49991,14 +50012,14 @@ static class E var mImplementation = e.GetMember("M"); AssertEx.Equal(""" - + """, mImplementation.GetDocumentationCommentXml()); var p = extension.GetMember("P"); AssertEx.Equal(""" - + Summary for P @@ -50007,7 +50028,7 @@ static class E var pGetImplementation = e.GetMember("get_P"); AssertEx.Equal(""" - + """, pGetImplementation.GetDocumentationCommentXml()); @@ -50023,22 +50044,22 @@ static class E Summary for E - + - + - + Summary for extension block Description for T Description for t - + Summary for M Description for U Description for u - + Summary for P @@ -50119,7 +50140,7 @@ static class E var extension = e.GetTypeMembers().Single(); AssertEx.Equal(""" - + Summary for extension block Description for T Description for t @@ -50129,7 +50150,7 @@ static class E var mSkeleton = extension.GetMember("M"); AssertEx.Equal(""" - + Summary for M Description for U Description for u @@ -50140,14 +50161,14 @@ static class E var mImplementation = e.GetMember("M"); AssertEx.Equal(""" - + """, mImplementation.GetDocumentationCommentXml()); var p = extension.GetMember("P"); AssertEx.Equal(""" - + Summary for P @@ -50156,7 +50177,7 @@ static class E var pGetImplementation = e.GetMember("get_P"); AssertEx.Equal(""" - + """, pGetImplementation.GetDocumentationCommentXml()); @@ -50232,28 +50253,28 @@ static class E var mSkeleton = extension.GetMember("M"); AssertEx.Equal(""" - + """, mSkeleton.GetDocumentationCommentXml()); var mImplementation = e.GetMember("M"); AssertEx.Equal(""" - + """, mImplementation.GetDocumentationCommentXml()); var p = extension.GetMember("P"); AssertEx.Equal(""" - + """, p.GetDocumentationCommentXml()); var pGetImplementation = e.GetMember("get_P"); AssertEx.Equal(""" - + """, pGetImplementation.GetDocumentationCommentXml()); @@ -50295,7 +50316,7 @@ static class E var extensions = e.GetTypeMembers(); AssertEx.Equal(""" - + First summary for extension block First description for T First description for t @@ -50307,7 +50328,7 @@ static class E """, extensions[0].GetDocumentationCommentXml()); AssertEx.Equal(""" - + First summary for extension block First description for T First description for t @@ -50326,12 +50347,12 @@ static class E - + - + - + First summary for extension block First description for T First description for t @@ -50339,10 +50360,10 @@ static class E Second description for T Second description for t - + First method - + Second method @@ -50379,7 +50400,7 @@ static class E foreach (var extension in extensions) { AssertEx.Equal(""" - + First summary for extension block Second summary for extension block @@ -50394,7 +50415,7 @@ static class E test - + First summary for extension block Second summary for extension block @@ -50438,7 +50459,7 @@ static class E var e = comp.GetMember("E"); var nestedExtension = e.GetTypeMembers().Single().GetTypeMembers().First(); - AssertEx.Equal("T:E.$C43E2675C7BBF9284AF22FB8A9BF0280.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", + AssertEx.Equal("T:E.$C43E2675C7BBF9284AF22FB8A9BF0280.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", nestedExtension.GetDocumentationCommentId()); var expected = """ @@ -50448,14 +50469,14 @@ static class E test - + First summary for extension block Second summary for extension block - + method - + First summary for extension block Second summary for extension block @@ -50505,13 +50526,13 @@ static class E2 - + - + First summary for extension block Second summary for extension block - + method @@ -50553,13 +50574,13 @@ public void XmlDoc_09() test - + First summary for extension block - + method - + Second summary for extension block @@ -50797,19 +50818,19 @@ public static class E var extension = libComp.GetMember("E").GetTypeMembers().Single(); Debug.Assert(extension.IsExtension); - AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); var p = extension.GetMember("P"); - AssertEx.Equal("P:E.$8048A6C8BE30A622530249B904B537EB.P", p.GetDocumentationCommentId()); + AssertEx.Equal("P:E.$8048A6C8BE30A622530249B904B537EB`1.P", p.GetDocumentationCommentId()); var comp = CreateCompilation("", references: [libComp.EmitToImageReference()]); extension = comp.GetMember("E").GetTypeMembers().Single(); Debug.Assert(extension.IsExtension); - AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); p = extension.GetMember("P"); - AssertEx.Equal("P:E.$8048A6C8BE30A622530249B904B537EB.P", p.GetDocumentationCommentId()); + AssertEx.Equal("P:E.$8048A6C8BE30A622530249B904B537EB`1.P", p.GetDocumentationCommentId()); } [Fact] @@ -50831,6 +50852,66 @@ static class E Diagnostic(ErrorCode.ERR_DuplicateTypeParameter, "T").WithArguments("T").WithLocation(4, 18)); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/80294")] + public void XmlDoc_17(bool withDocumentationProvider) + { + string source = """ +public static class E +{ + extension(int i) + { + public void M() { } + } +} +""" + ExtensionMarkerAttributeDefinition; + + var moduleComp = CreateCompilation(source, options: TestOptions.ReleaseModule, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + var moduleRef = moduleComp.EmitToImageReference(documentation: withDocumentationProvider ? new TestDocumentationProvider() : null); + + var comp = CreateCompilation("", references: [moduleRef], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var e = comp.GlobalNamespace.GetTypeMember("E"); + Assert.Equal("", e.GetDocumentationCommentXml()); + + var extension = e.GetTypeMembers().Single(); + Assert.True(extension.IsExtension); + Assert.Equal("", extension.GetDocumentationCommentXml()); + Assert.Equal("", extension.ContainingSymbol.GetDocumentationCommentXml()); + + Assert.Equal("", comp.GlobalNamespace.GetDocumentationCommentXml()); + + // Baseline without extensions + source = """ +/// Summary +public class C { } +"""; + + moduleComp = CreateCompilation(source, options: TestOptions.ReleaseModule, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + moduleRef = moduleComp.EmitToImageReference(documentation: withDocumentationProvider ? new TestDocumentationProvider() : null); + string source2 = """ +/// Summary for D +public class D { } +"""; + + comp = CreateCompilation(source2, assemblyName: "name", references: [moduleRef], parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + AssertEx.Equal(""" + + + + name + + + + Summary for D + + + +""", GetDocumentationCommentText(comp)); + } + [Fact] public void XmlDoc_Param_01() { @@ -51341,9 +51422,9 @@ public void M() { } "M -> void E.$8048A6C8BE30A622530249B904B537EB.M()", "Attr3 -> System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P { get; }", "P -> System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P { get; }", - "T -> E.$8048A6C8BE30A622530249B904B537EB", - "Attr -> E.$8048A6C8BE30A622530249B904B537EB", - "extension -> E.$8048A6C8BE30A622530249B904B537EB"], + "T -> E.$8048A6C8BE30A622530249B904B537EB.$4294E9E080371DCE7DAC7C951C4773A1", + "Attr -> E.$8048A6C8BE30A622530249B904B537EB.$4294E9E080371DCE7DAC7C951C4773A1", + "extension -> E.$8048A6C8BE30A622530249B904B537EB.$4294E9E080371DCE7DAC7C951C4773A1"], analyzer._results.ToArray()); } @@ -51406,11 +51487,11 @@ public void M(int i) { } AssertEx.SetEqual([ "E", - "E.$8048A6C8BE30A622530249B904B537EB", + "E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", "System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P { get; }", "T t", - "E.$865F3E9780C1FF12019ECA0B40816384", - "E.$C43E2675C7BBF9284AF22FB8A9BF0280", + "E.$865F3E9780C1FF12019ECA0B40816384.$865F3E9780C1FF12019ECA0B40816384", + "E.$C43E2675C7BBF9284AF22FB8A9BF0280.$64C7DA4F599E1426EA88DEA0AE9DC8CD", "System.Object o1", "void E.$8048A6C8BE30A622530249B904B537EB.M(System.Int32 i)", "System.Int32 i", @@ -51506,14 +51587,14 @@ public void M(int i) { } AssertEx.SetEqual([ "Start: E", - "Start: E.$8048A6C8BE30A622530249B904B537EB", + "Start: E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", "Start: void E.$8048A6C8BE30A622530249B904B537EB.M(System.Int32 i)", "Start: System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P { get; }", "Start: System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P.get", "End: System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P { get; }", "End: System.Int32 E.$8048A6C8BE30A622530249B904B537EB.P.get", "End: void E.$8048A6C8BE30A622530249B904B537EB.M(System.Int32 i)", - "End: E.$8048A6C8BE30A622530249B904B537EB", + "End: E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", "End: E"], analyzer._results.ToArray()); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs index 1b8dd6f1176..8a49e66f9e6 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs @@ -4,9 +4,9 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Globalization; using System.Linq; +using System.Reflection.Metadata.Ecma335; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; @@ -76,6 +76,102 @@ static class E Diagnostic(ErrorCode.ERR_MissingDeconstruct, @"""""").WithArguments("string", "2").WithLocation(1, 14)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_01() + { + var source = """ + var (a, b, c) = 42; + var ((d, _, _), (_, e, _), (_, _, f)) = 42; + System.Console.WriteLine($"{a} {b} {c} {d} {e} {f}"); + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + } + } + """; + CompileAndVerify(source, expectedOutput: "1 2 3 1 2 3").VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_02() + { + var source = """ + ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,73): error CS7036: There is no argument given that corresponds to the required parameter 'c' of 'E.extension(int).Deconstruct(out int, out int, out int)' + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "42").WithArguments("c", "E.extension(int).Deconstruct(out int, out int, out int)").WithLocation(1, 73), + // (1,73): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type. + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "2").WithLocation(1, 73), + // (1,73): error CS1501: No overload for method 'Deconstruct' takes 4 arguments + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_BadArgCount, "42").WithArguments("Deconstruct", "4").WithLocation(1, 73), + // (1,73): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 4 out parameters and a void return type. + // ((int a, int b, int c), (int d, int e), (int f, int g, int h, int i)) = 42; + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "4").WithLocation(1, 73)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_03() + { + var source = """ + ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42; + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,96): error CS1501: No overload for method 'Deconstruct' takes 4 arguments + // ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42; + Diagnostic(ErrorCode.ERR_BadArgCount, "42").WithArguments("Deconstruct", "4").WithLocation(1, 96), + // (1,96): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 4 out parameters and a void return type. + // ((int d, int _, int _), (int _, int e, int _), (int _, int _, int f), (int _, int _, int _)) = 42; + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "42").WithArguments("int", "4").WithLocation(1, 96)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80217")] + public void Deconstruct_Nested_04() + { + var source = """ + var ((a, b, c), (d, e), (f, g)) = 42; + System.Console.WriteLine($"{a} {b} {c} {d} {e} {f} {g}"); + + static class E + { + extension(int instance) + { + public void Deconstruct(out int a, out int b, out int c) + => (a, b, c) = (1, 2, 3); + + public void Deconstruct(out int a, out int b) + => (a, b) = (4, 5); + } + } + """; + CompileAndVerify(source, expectedOutput: "1 2 3 4 5 4 5").VerifyDiagnostics(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75484")] public void Deconstruction_UnscopedRef_ExtensionMethod() { @@ -619,6 +715,73 @@ public static class E CompileAndVerify(comp, expectedOutput: "method 42").VerifyDiagnostics(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78968")] + public void AnonymousType_03() + { + var src = """ +42.M(); + +public static class E +{ + extension(int i) + { + public void M() + { + var x = new { A = 1 }; + local(x, x => new { x.A }); + + void local(U u, System.Linq.Expressions.Expression> f) + { + System.Console.Write(f.Compile()(u)); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("{ A = 1 }"), verify: Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78968")] + public void AnonymousType_04() + { + var src = """ +42.M(43); + +public static class E +{ + extension(int i) + { + public void M(T t) + { + var x = new { A = t }; + local(x, x => new { x.A }); + + static void local(U u, System.Linq.Expressions.Expression> f) + { + System.Console.Write(f.Compile()(u)); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("{ A = 43 }"), verify: Verification.Skipped).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80250")] + public void Cls() + { + var src = """ + [assembly: System.CLSCompliant(true)] + public static class Extensions + { + extension(object) { } + } + """; + CreateCompilation(src).VerifyEmitDiagnostics(); + } + [Fact] public void Attribute_01() { @@ -4108,7 +4271,7 @@ .method public hidebysig specialname rtspecialname instance void .ctor () cil ma var comp = CreateCompilationWithIL(src, ilSrc); comp.VerifyEmitDiagnostics(); var extension = (PENamedTypeSymbol)comp.GetMember("E").GetTypeMembers().Single(); - AssertEx.Equal("E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", extension.ToTestDisplayString()); + AssertEx.Equal("E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", extension.ToTestDisplayString()); AssertEx.SetEqual([], extension.GetAttributes().Select(a => a.ToString())); Assert.Equal("", extension.Name); AssertEx.Equal("$BA41CFE2B5EDAEB8C1B9062F59ED4D69", extension.MetadataName); @@ -4135,7 +4298,9 @@ public static void M() { } var extension = (PENamedTypeSymbol)comp.GetMember("E").GetTypeMembers().Single(); Assert.True(extension.IsExtension); - AssertEx.Equal("E.$8048A6C8BE30A622530249B904B537EB", extension.ToTestDisplayString()); + Assert.Equal("$8048A6C8BE30A622530249B904B537EB", extension.ExtensionGroupingName); + Assert.Equal("$01CE3801593377B4E240F33E20D30D50", extension.ExtensionMarkerName); + AssertEx.Equal("E.$8048A6C8BE30A622530249B904B537EB.$01CE3801593377B4E240F33E20D30D50", extension.ToTestDisplayString()); Assert.Equal("", extension.Name); AssertEx.Equal("$01CE3801593377B4E240F33E20D30D50", extension.MetadataName); Assert.False(extension.MangleName); @@ -4944,12 +5109,12 @@ static class E var e = comp.GetMember("E"); AssertEx.Equal(""" - + """, e.GetDocumentationCommentXml()); - AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", e.GetTypeMembers().Single().GetDocumentationCommentId()); var tree = comp.SyntaxTrees.Single(); @@ -5606,7 +5771,7 @@ static class E var e = comp.GetMember("E"); AssertEx.Equal(""" - + """, e.GetDocumentationCommentXml()); @@ -5665,7 +5830,7 @@ static class E var e = comp.GetMember("E"); AssertEx.Equal(""" - + """, e.GetDocumentationCommentXml()); @@ -6510,10 +6675,10 @@ public static void M() { } AssertEx.SequenceEqual(["(E.extension(int).@M, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M())"], PrintXmlCrefSymbols(tree, model)); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78967")] public void Cref_52() { - // unqualified reference + // unqualified extension block var src = """ /// /// @@ -6535,50 +6700,25 @@ public static void Method() { } { } - /// + /// /// public static void M2() { } } """; - // Tracked by https://github.com/dotnet/roslyn/issues/78967 : cref, such unqualified references in CREF should work within context of enclosing static type var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); - comp.VerifyEmitDiagnostics( - // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(1, 16), - // (2,16): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(2, 16), - // (7,24): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(7, 24), - // (8,24): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(8, 24), - // (15,20): warning CS1574: XML comment has cref attribute 'extension(int).Method' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Method").WithArguments("extension(int).Method").WithLocation(15, 20), - // (16,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(16, 20), - // (21,20): warning CS1574: XML comment has cref attribute 'extension(int).M2' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M2").WithArguments("extension(int).M2").WithLocation(21, 20), - // (22,20): warning CS1574: XML comment has cref attribute 'extension(int).Property' that could not be resolved - // /// - Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).Property").WithArguments("extension(int).Property").WithLocation(22, 20)); + comp.VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); AssertEx.Equal([ - "(extension(int).Method, null)", - "(extension(int).Property, null)", - "(extension(int).Method, null)", - "(extension(int).Property, null)", - "(extension(int).Method, null)", - "(extension(int).Property, null)", - "(extension(int).M2, null)", - "(extension(int).Property, null)"], + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })", + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })", + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })", + "(extension(int).Method, void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Method())", + "(extension(int).Property, System.Int32 E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.Property { get; })"], PrintXmlCrefSymbols(tree, model)); src = """ @@ -6695,149 +6835,474 @@ static class E } [Fact] - public void PropertyAccess_Set_01() + public void Cref_56() { var src = """ +/// static class E { - extension(S1 x) + extension(ref int i) { - public int P1 - { - get - { - return 0; - } - set - { - System.Console.Write(x.F1); - } - } + public static void M() => throw null!; } } +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int).M()").WithArguments("extension(int).M()").WithLocation(1, 16)); -public struct S1 + var src2 = """ +/// +static class E { - public int F1; - - public void Test() + extension(int i) { - this.P1 = Program.Get1(); + public static void M() => throw null!; } +} +"""; + var comp2 = CreateCompilation(src2, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp2.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(ref int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(ref int).M()").WithArguments("extension(ref int).M()").WithLocation(1, 16)); - public int P2 - { - get - { - return 0; - } - set - { - System.Console.Write(F1); - } + var extension = comp.GetMember("E").GetTypeMembers().Single(); + var extension2 = comp2.GetMember("E").GetTypeMembers().Single(); + Assert.True(extension.ExtensionGroupingName == extension2.ExtensionGroupingName); } - public void Test2() + [Fact] + public void Cref_57() { - this.P2 = Program.Get1(); - } - - public void Test3() + var src = """ +/// +static class E +{ + extension(int) { - E.set_P1(this, Program.Get1()); + public static void M() => throw null!; } } - -class Program -{ - public static S1 F; - - static void Main() - { - F = new S1 { F1 = 123 }; - Test(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test2(); - System.Console.Write(F.F1); - - System.Console.Write(":"); - - F = new S1 { F1 = 123 }; - F.Test3(); - System.Console.Write(F.F1); +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension(int)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "E.extension(int)").WithArguments("extension(int)").WithLocation(1, 16)); } - static void Test() + [Fact] + public void Cref_58() { - F.P1 = Get1(); - } - - public static int Get1() + var src = """ +static class E +{ + extension(int) { - Program.F.F1++; - return 1; + /// + public static void M() => throw null!; } } """; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); - var comp = CreateCompilation(src, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124:124124:123124", verify: Verification.Skipped).VerifyDiagnostics(); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension(int).M(), void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M())"], + PrintXmlCrefSymbols(tree, model)); + } - verifier.VerifyIL("Program.Test", -@" + [Fact] + public void Cref_59() + { + var src = """ +static class E { - // Code size 25 (0x19) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: ldsflda ""S1 Program.F"" - IL_0006: call ""int Program.Get1()"" - IL_000b: stloc.0 - IL_000c: ldobj ""S1"" - IL_0011: ldloc.0 - IL_0012: call ""void E.set_P1(S1, int)"" - IL_0017: nop - IL_0018: ret + /// + extension(T) + { + /// + public static void M() => throw null!; + } } -"); +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); - verifier.VerifyIL("S1.Test", -@" -{ - // Code size 21 (0x15) - .maxstack 2 - .locals init (int V_0) - IL_0000: nop - IL_0001: ldarg.0 - IL_0002: call ""int Program.Get1()"" - IL_0007: stloc.0 - IL_0008: ldobj ""S1"" - IL_000d: ldloc.0 - IL_000e: call ""void E.set_P1(S1, int)"" - IL_0013: nop - IL_0014: ret -} -"); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension{U}(U).M(), void E.$8048A6C8BE30A622530249B904B537EB.M())", + "(extension{V}(V).M(), void E.$8048A6C8BE30A622530249B904B537EB.M())"], + PrintXmlCrefSymbols(tree, model)); + } - var src2 = $$$""" + [Fact] + public void Cref_60() + { + var src = """ static class E { - extension(S1 x) + /// + extension(int) { - public int P1 { get => 0; set {} } + /// + public static void M(T t) => throw null!; } } - +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension{U}(int).M(U), void E.$B8D310208B4544F25EEBACB9990FC73B.M(U t))", + "(extension{V}(int).M(V), void E.$B8D310208B4544F25EEBACB9990FC73B.M(V t))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_61() + { + // in namespace + var src = """ +namespace E +{ + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 5), + // (3,5): warning CS1591: Missing XML comment for publicly visible type or member 'extension(int)' + // extension(int) + Diagnostic(ErrorCode.WRN_MissingXMLComment, "extension").WithArguments("E.extension(int)").WithLocation(3, 5), + // (5,24): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(5, 24)); + } + + [Fact] + public void Cref_62() + { + // top-level extension block + var src = """ +/// +extension(int) +{ + /// + public static void M(T t) => throw null!; +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (1,16): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(1, 16), + // (2,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(2, 1), + // (4,20): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(4, 20)); + } + + [Fact] + public void Cref_63() + { + var src = """ +static class E +{ + extension(object) + { + public static void M1() => throw null!; + + extension(int) + { + /// + /// + public static void M2(T t) => throw null!; + } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (7,9): error CS9282: This member is not allowed in an extension block + // extension(int) + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(7, 9), + // (9,28): warning CS1574: XML comment has cref attribute 'extension{U}(int).M(U)' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension{U}(int).M(U)").WithArguments("extension{U}(int).M(U)").WithLocation(9, 28), + // (10,28): warning CS1574: XML comment has cref attribute 'extension(object).M1()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(object).M1()").WithArguments("extension(object).M1()").WithLocation(10, 28)); + } + + [Fact] + public void Cref_64() + { + // generic static enclosing class + var src = """ +static class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (4,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(4, 5)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension{U}(int).M(U), void E.$B8D310208B4544F25EEBACB9990FC73B.M(U t))", + "(extension{U}(int).M(U), void E.$B8D310208B4544F25EEBACB9990FC73B.M(U t))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_65() + { + // non-static enclosing class + var src = """ +class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (4,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(4, 5)); + } + + [Fact] + public void Cref_66() + { + var src = """ +static class E +{ + /// + extension(int) + { + /// + public static void M(T t) => throw null!; + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + AssertEx.Equal([ + "(extension(int).M{U}(U), void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M(U t))", + "(extension(int).M{U}(U), void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M(U t))"], + PrintXmlCrefSymbols(tree, model)); + } + + [Fact] + public void Cref_67() + { + var src = """ +static class E +{ + extension(int) + { + public static void M() => throw null!; + + /// + class Nested + { + /// + public static void Method() => throw null!; + } + } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.RegularPreviewWithDocumentationComments); + comp.VerifyEmitDiagnostics( + // (7,24): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M()").WithArguments("extension(int).M()").WithLocation(7, 24), + // (8,15): error CS9282: This member is not allowed in an extension block + // class Nested + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(8, 15), + // (10,28): warning CS1574: XML comment has cref attribute 'extension(int).M()' that could not be resolved + // /// + Diagnostic(ErrorCode.WRN_BadXMLRef, "extension(int).M()").WithArguments("extension(int).M()").WithLocation(10, 28)); + } + + [Fact] + public void PropertyAccess_Set_01() + { + var src = """ +static class E +{ + extension(S1 x) + { + public int P1 + { + get + { + return 0; + } + set + { + System.Console.Write(x.F1); + } + } + } +} + +public struct S1 +{ + public int F1; + + public void Test() + { + this.P1 = Program.Get1(); + } + + public int P2 + { + get + { + return 0; + } + set + { + System.Console.Write(F1); + } + } + + public void Test2() + { + this.P2 = Program.Get1(); + } + + public void Test3() + { + E.set_P1(this, Program.Get1()); + } +} + +class Program +{ + public static S1 F; + + static void Main() + { + F = new S1 { F1 = 123 }; + Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test2(); + System.Console.Write(F.F1); + + System.Console.Write(":"); + + F = new S1 { F1 = 123 }; + F.Test3(); + System.Console.Write(F.F1); + } + + static void Test() + { + F.P1 = Get1(); + } + + public static int Get1() + { + Program.F.F1++; + return 1; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "124124:124124:124124:123124", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier.VerifyIL("Program.Test", +@" +{ + // Code size 25 (0x19) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldsflda ""S1 Program.F"" + IL_0006: call ""int Program.Get1()"" + IL_000b: stloc.0 + IL_000c: ldobj ""S1"" + IL_0011: ldloc.0 + IL_0012: call ""void E.set_P1(S1, int)"" + IL_0017: nop + IL_0018: ret +} +"); + + verifier.VerifyIL("S1.Test", +@" +{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""int Program.Get1()"" + IL_0007: stloc.0 + IL_0008: ldobj ""S1"" + IL_000d: ldloc.0 + IL_000e: call ""void E.set_P1(S1, int)"" + IL_0013: nop + IL_0014: ret +} +"); + + var src2 = $$$""" +static class E +{ + extension(S1 x) + { + public int P1 { get => 0; set {} } + } +} + struct S1; class Program @@ -7117,8 +7582,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -7460,8 +7923,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -7884,6 +8345,58 @@ .locals init (T V_0, IL_0035: ret } "); + + comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0xa } + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0x32 } + [Main]: Return value missing on the stack. { Offset = 0x41 } + [Initialize]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Initialize]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Increment]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Increment]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Test1]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Test3]: Return value missing on the stack. { Offset = 0x47 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x2a, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003c + IL_0039: ldloc.0 + IL_003a: br.s IL_0041 + IL_003c: ldsfld "T Program.F" + IL_0041: ldloc.1 + IL_0042: call "void E.set_P1(T, int)" + IL_0047: ret + } + """); } [Theory] @@ -8568,6 +9081,64 @@ .locals init (int V_0) IL_0020: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa4 } + [Test3]: Return value missing on the stack. { Offset = 0x67 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int E.get_P1(T)" + IL_003b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0040: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloca.s V_3 + IL_004b: initobj "T" + IL_0051: ldloc.3 + IL_0052: box "T" + IL_0057: brtrue.s IL_005c + IL_0059: ldloc.0 + IL_005a: br.s IL_0061 + IL_005c: ldsfld "T Program.F" + IL_0061: ldloc.2 + IL_0062: call "void E.set_P1(T, int)" + IL_0067: ret + } + """); } [Fact] @@ -8666,6 +9237,38 @@ .maxstack 3 } "); + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6a } + [Test3]: Return value missing on the stack. { Offset = 0x23 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 36 (0x24) + .maxstack 3 + .locals init (int V_0, + int V_1) + IL_0000: ldsflda "T Program.F" + IL_0005: call "int E.get_P1(ref T)" + IL_000a: stloc.0 + IL_000b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0010: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0015: stloc.1 + IL_0016: ldsflda "T Program.F" + IL_001b: ldloc.0 + IL_001c: ldloc.1 + IL_001d: add + IL_001e: call "void E.set_P1(ref T, int)" + IL_0023: ret + } + """); + var src2 = """ static class E { @@ -8862,6 +9465,64 @@ .maxstack 3 IL_0019: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test3]: Return value missing on the stack. { Offset = 0x67 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int E.get_P1(T)" + IL_003b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0040: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloca.s V_3 + IL_004b: initobj "T" + IL_0051: ldloc.3 + IL_0052: box "T" + IL_0057: brtrue.s IL_005c + IL_0059: ldloc.0 + IL_005a: br.s IL_0061 + IL_005c: ldsfld "T Program.F" + IL_0061: ldloc.2 + IL_0062: call "void E.set_P1(T, int)" + IL_0067: ret + } + """); } [Fact] @@ -8956,8 +9617,9 @@ static async Task Get1Async() } """; + var expectedOutput = "123125125:123125125"; var comp = CreateCompilation(src, options: TestOptions.DebugExe.WithAllowUnsafe(true)); - var verifier = CompileAndVerify(comp, expectedOutput: "123125125:123125125", verify: Verification.Skipped).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Skipped).VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" @@ -8995,6 +9657,71 @@ .locals init (T V_0, IL_0041: ret } "); + + comp = CreateRuntimeAsyncCompilation(src, options: TestOptions.UnsafeReleaseExe.WithSpecificDiagnosticOptions("SYSLIB5007", ReportDiagnostic.Suppress)); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0xa } + [Main]: Cannot change initonly field outside its .ctor. { Offset = 0x32 } + [Main]: Return value missing on the stack. { Offset = 0x41 } + [Initialize]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Initialize]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Increment]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Increment]: Expected numeric type on the stack. { Offset = 0xc, Found = address of Int32 } + [Test1]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } + [Test3]: Return value missing on the stack. { Offset = 0x67 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x2a, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 104 (0x68) + .maxstack 2 + .locals init (T V_0, + int V_1, + int V_2, + T V_3) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int E.get_P1(T)" + IL_003b: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0040: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0045: stloc.1 + IL_0046: ldloc.1 + IL_0047: add + IL_0048: stloc.2 + IL_0049: ldloca.s V_3 + IL_004b: initobj "T" + IL_0051: ldloc.3 + IL_0052: box "T" + IL_0057: brtrue.s IL_005c + IL_0059: ldloc.0 + IL_005a: br.s IL_0061 + IL_005c: ldsfld "T Program.F" + IL_0061: ldloc.2 + IL_0062: call "void E.set_P1(T, int)" + IL_0067: ret + } + """); } [Theory] @@ -11629,6 +12356,121 @@ .locals init (T& V_0, IL_003d: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x152 } + [Test13]: Return value missing on the stack. { Offset = 0x6d } + [Test23]: Return value missing on the stack. { Offset = 0x7f } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test13()", """ + { + // Code size 110 (0x6e) + .maxstack 3 + .locals init (T V_0, + T V_1, + object V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: initobj "T" + IL_0008: ldloc.1 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_1 + IL_0020: initobj "T" + IL_0026: ldloc.1 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "object E.get_P1(T)" + IL_003b: brtrue.s IL_006d + IL_003d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0047: box "int" + IL_004c: stloc.2 + IL_004d: ldloca.s V_1 + IL_004f: initobj "T" + IL_0055: ldloc.1 + IL_0056: box "T" + IL_005b: brtrue.s IL_0060 + IL_005d: ldloc.0 + IL_005e: br.s IL_0065 + IL_0060: ldsfld "T Program.F" + IL_0065: ldloc.2 + IL_0066: dup + IL_0067: stloc.3 + IL_0068: call "void E.set_P1(T, object)" + IL_006d: ret + } + """); + + verifier.VerifyIL("Program.Test23()", """ + { + // Code size 128 (0x80) + .maxstack 3 + .locals init (T V_0, + int? V_1, + int V_2, + T V_3, + int? V_4) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int? E.get_P2(T)" + IL_003b: stloc.1 + IL_003c: ldloca.s V_1 + IL_003e: call "readonly int int?.GetValueOrDefault()" + IL_0043: stloc.2 + IL_0044: ldloca.s V_1 + IL_0046: call "readonly bool int?.HasValue.get" + IL_004b: brtrue.s IL_007f + IL_004d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0052: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.2 + IL_0058: ldloca.s V_3 + IL_005a: initobj "T" + IL_0060: ldloc.3 + IL_0061: box "T" + IL_0066: brtrue.s IL_006b + IL_0068: ldloc.0 + IL_0069: br.s IL_0070 + IL_006b: ldsfld "T Program.F" + IL_0070: ldloca.s V_4 + IL_0072: ldloc.2 + IL_0073: call "int?..ctor(int)" + IL_0078: ldloc.s V_4 + IL_007a: call "void E.set_P2(T, int?)" + IL_007f: ret + } + """); } [Fact] @@ -11804,6 +12646,69 @@ .locals init (T& V_0, } "); + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xde } + [Test13]: Return value missing on the stack. { Offset = 0x29 } + [Test23]: Return value missing on the stack. { Offset = 0x3a } + [Get1Async]: Unexpected type on the stack. { Offset = 0x34, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test13()", """ + { + // Code size 42 (0x2a) + .maxstack 3 + .locals init (int V_0, + object V_1) + IL_0000: ldsflda "T Program.F" + IL_0005: call "object E.get_P1(ref T)" + IL_000a: brtrue.s IL_0029 + IL_000c: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0011: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0016: stloc.0 + IL_0017: ldsflda "T Program.F" + IL_001c: ldloc.0 + IL_001d: box "int" + IL_0022: dup + IL_0023: stloc.1 + IL_0024: call "void E.set_P1(ref T, object)" + IL_0029: ret + } + """); + + verifier.VerifyIL("Program.Test23()", """ + { + // Code size 59 (0x3b) + .maxstack 3 + .locals init (int? V_0, + int V_1, + int? V_2) + IL_0000: ldsflda "T Program.F" + IL_0005: call "int? E.get_P2(ref T)" + IL_000a: stloc.0 + IL_000b: ldloca.s V_0 + IL_000d: call "readonly int int?.GetValueOrDefault()" + IL_0012: stloc.1 + IL_0013: ldloca.s V_0 + IL_0015: call "readonly bool int?.HasValue.get" + IL_001a: brtrue.s IL_003a + IL_001c: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0021: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0026: stloc.1 + IL_0027: ldsflda "T Program.F" + IL_002c: ldloca.s V_2 + IL_002e: ldloc.1 + IL_002f: call "int?..ctor(int)" + IL_0034: ldloc.2 + IL_0035: call "void E.set_P2(ref T, int?)" + IL_003a: ret + } + """); + var src2 = """ static class E { @@ -12156,6 +13061,121 @@ .locals init (T V_0, IL_0038: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x134 } + [Test13]: Return value missing on the stack. { Offset = 0x6d } + [Test23]: Return value missing on the stack. { Offset = 0x7f } + [Get1Async]: Unexpected type on the stack. { Offset = 0x41, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test13()", """ + { + // Code size 110 (0x6e) + .maxstack 3 + .locals init (T V_0, + T V_1, + object V_2, + object V_3) + IL_0000: ldloca.s V_1 + IL_0002: initobj "T" + IL_0008: ldloc.1 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_1 + IL_0020: initobj "T" + IL_0026: ldloc.1 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "object E.get_P1(T)" + IL_003b: brtrue.s IL_006d + IL_003d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0042: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0047: box "int" + IL_004c: stloc.2 + IL_004d: ldloca.s V_1 + IL_004f: initobj "T" + IL_0055: ldloc.1 + IL_0056: box "T" + IL_005b: brtrue.s IL_0060 + IL_005d: ldloc.0 + IL_005e: br.s IL_0065 + IL_0060: ldsfld "T Program.F" + IL_0065: ldloc.2 + IL_0066: dup + IL_0067: stloc.3 + IL_0068: call "void E.set_P1(T, object)" + IL_006d: ret + } + """); + + verifier.VerifyIL("Program.Test23()", """ + { + // Code size 128 (0x80) + .maxstack 3 + .locals init (T V_0, + int? V_1, + int V_2, + T V_3, + int? V_4) + IL_0000: ldloca.s V_3 + IL_0002: initobj "T" + IL_0008: ldloc.3 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: ldloca.s V_3 + IL_0020: initobj "T" + IL_0026: ldloc.3 + IL_0027: box "T" + IL_002c: brtrue.s IL_0031 + IL_002e: ldloc.0 + IL_002f: br.s IL_0036 + IL_0031: ldsfld "T Program.F" + IL_0036: call "int? E.get_P2(T)" + IL_003b: stloc.1 + IL_003c: ldloca.s V_1 + IL_003e: call "readonly int int?.GetValueOrDefault()" + IL_0043: stloc.2 + IL_0044: ldloca.s V_1 + IL_0046: call "readonly bool int?.HasValue.get" + IL_004b: brtrue.s IL_007f + IL_004d: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0052: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0057: stloc.2 + IL_0058: ldloca.s V_3 + IL_005a: initobj "T" + IL_0060: ldloc.3 + IL_0061: box "T" + IL_0066: brtrue.s IL_006b + IL_0068: ldloc.0 + IL_0069: br.s IL_0070 + IL_006b: ldsfld "T Program.F" + IL_0070: ldloca.s V_4 + IL_0072: ldloc.2 + IL_0073: call "int?..ctor(int)" + IL_0078: ldloc.s V_4 + IL_007a: call "void E.set_P2(T, int?)" + IL_007f: ret + } + """); } [Fact] @@ -12628,6 +13648,51 @@ .locals init (int V_0) IL_0014: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0xa4 } + [Test3]: Return value missing on the stack. { Offset = 0x47 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003c + IL_0039: ldloc.0 + IL_003a: br.s IL_0041 + IL_003c: ldsfld "T Program.F" + IL_0041: ldloc.1 + IL_0042: call "void E.set_P1(T, int)" + IL_0047: ret + } + """); } [Fact] @@ -12726,6 +13791,32 @@ .locals init (int V_0) } "); + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x6a } + [Test3]: Return value missing on the stack. { Offset = 0x16 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 23 (0x17) + .maxstack 2 + .locals init (int V_0) + IL_0000: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0005: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_000a: stloc.0 + IL_000b: ldsflda "T Program.F" + IL_0010: ldloc.0 + IL_0011: call "void E.set_P1(ref T, int)" + IL_0016: ret + } + """); + var src2 = """ static class E { @@ -12918,6 +14009,51 @@ .locals init (int V_0) IL_0014: ret } "); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x95 } + [Test3]: Return value missing on the stack. { Offset = 0x47 } + [Get1Async]: Unexpected type on the stack. { Offset = 0x50, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + + verifier.VerifyIL("Program.Test3()", """ + { + // Code size 72 (0x48) + .maxstack 2 + .locals init (T V_0, + int V_1, + T V_2) + IL_0000: ldloca.s V_2 + IL_0002: initobj "T" + IL_0008: ldloc.2 + IL_0009: box "T" + IL_000e: brtrue.s IL_0018 + IL_0010: ldsfld "T Program.F" + IL_0015: stloc.0 + IL_0016: br.s IL_001e + IL_0018: ldsfld "T Program.F" + IL_001d: pop + IL_001e: call "System.Threading.Tasks.Task Program.Get1Async()" + IL_0023: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0028: stloc.1 + IL_0029: ldloca.s V_2 + IL_002b: initobj "T" + IL_0031: ldloc.2 + IL_0032: box "T" + IL_0037: brtrue.s IL_003c + IL_0039: ldloc.0 + IL_003a: br.s IL_0041 + IL_003c: ldsfld "T Program.F" + IL_0041: ldloc.1 + IL_0042: call "void E.set_P1(T, int)" + IL_0047: ret + } + """); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/78829")] @@ -15229,16 +16365,13 @@ struct S1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new S1 { F1 = 123 }; @@ -15253,22 +16386,21 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[Get1(), $""] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[Get1(), $""] += await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; var comp = CreateCompilation([src, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute], options: TestOptions.DebugExe); - var verifier = CompileAndVerify(comp, expectedOutput: "123123123").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "123123:123123").VerifyDiagnostics(); verifier.VerifyIL("Program.Test1()", @" @@ -15353,8 +16485,6 @@ class C1 class Program { -// https://github.com/dotnet/roslyn/issues/79415 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Test1(); @@ -15363,10 +16493,9 @@ static async Task Main() Test2(); - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //System.Console.Write(":"); + System.Console.Write(":"); - //await Test3(); + await Test3(); } static T GetT() => (T)(object)new C1 { F1 = 123 }; @@ -15386,17 +16515,16 @@ static int Get1() return 1; } - // https://github.com/dotnet/roslyn/issues/79415 - uncomment the following code once fixed - //static async Task Test3() - //{ - // GetT()[Get1(), $""] += await Get1Async(); - //} + static async Task Test3() + { + GetT()[Get1(), $""] += await Get1Async(); + } - //static async Task Get1Async() - //{ - // await Task.Yield(); - // return 1; - //} + static async Task Get1Async() + { + await Task.Yield(); + return 1; + } } """; @@ -15971,8 +17099,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -16402,8 +17528,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -17212,8 +18336,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -17570,8 +18692,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -18994,8 +20114,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -19348,8 +20466,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -19976,8 +21092,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new S1 { F1 = 123 }; @@ -20259,8 +21373,6 @@ class Program class Program { -// https://github.com/dotnet/roslyn/issues/79416 - remove the pragma once fixed -#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously static async Task Main() { Program.F = new C1 { F1 = 123 }; @@ -21209,7 +22321,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB'<$T0> + .class nested public auto ansi sealed specialname '$8048A6C8BE30A622530249B904B537EB`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -21234,7 +22346,7 @@ .maxstack 8 IL_0000: ret } // end of method '$A93DBF9EBD61C29E8B5CFA979E4C33E8'::'$' } // end of class $A93DBF9EBD61C29E8B5CFA979E4C33E8 - } // end of class $8048A6C8BE30A622530249B904B537EB + } // end of class $8048A6C8BE30A622530249B904B537EB`1 } // end of class E """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } @@ -21584,7 +22696,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$BAC44226FEFE1ED1B549A4B5F35748C7' + .class nested public auto ansi sealed specialname '$BAC44226FEFE1ED1B549A4B5F35748C7`1' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -21617,7 +22729,7 @@ .maxstack 8 IL_0000: ret } // end of method '$03A00A6A168488BDF2B2E5B73B8099A6'::'$' } // end of class $03A00A6A168488BDF2B2E5B73B8099A6 - } // end of class $BAC44226FEFE1ED1B549A4B5F35748C7 + } // end of class $BAC44226FEFE1ED1B549A4B5F35748C7`1 } // end of class E """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); @@ -23362,7 +24474,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$0AD8C3962A3C5E6BFA97E099F6F428C4'<(I) $T0, (I) $T1, (I) $T2> + .class nested public auto ansi sealed specialname '$0AD8C3962A3C5E6BFA97E099F6F428C4`3'<(I) $T0, (I) $T1, (I) $T2> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -23381,10 +24493,10 @@ 01 00 01 00 00 01 00 02 00 00 ) // Methods - .method private hidebysig specialname static + .method private hidebysig specialname static void '$' ( int32 '' - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -23395,7 +24507,7 @@ .maxstack 8 IL_0000: ret } // end of method '$5B198AEBE2F597134BE1E94D84704187'::'$' } // end of class $5B198AEBE2F597134BE1E94D84704187 - } // end of class $0AD8C3962A3C5E6BFA97E099F6F428C4 + } // end of class $0AD8C3962A3C5E6BFA97E099F6F428C4`3 } // end of class E """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } @@ -23457,7 +24569,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$B8D310208B4544F25EEBACB9990FC73B'<$T0> + .class nested public auto ansi sealed specialname '$B8D310208B4544F25EEBACB9990FC73B`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -23475,10 +24587,10 @@ 01 00 00 00 01 00 00 00 ) // Methods - .method private hidebysig specialname static + .method private hidebysig specialname static void '$' ( int32 '' - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -23496,7 +24608,7 @@ .maxstack 8 IL_0000: ret } // end of method '$D131137B02074799BD78183FB29034EC'::'$' } // end of class $D131137B02074799BD78183FB29034EC - } // end of class $B8D310208B4544F25EEBACB9990FC73B + } // end of class $B8D310208B4544F25EEBACB9990FC73B`1 } // end of class E """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); @@ -25921,9 +27033,9 @@ static void validate(ModuleSymbol module) var reader = ((PEModuleSymbol)module).Module.GetMetadataReader(); AssertEx.Equal([ "TypeDefinition:E", - "TypeDefinition:$66F77D1E46F965A5B22D4932892FA78B", + "TypeDefinition:$66F77D1E46F965A5B22D4932892FA78B`1", "TypeDefinition:$C8718A1AD9DFC47EBD0C706B9E6984E6", - "TypeDefinition:$BCF902721DDD961E5243C324D8379E5C", + "TypeDefinition:$BCF902721DDD961E5243C324D8379E5C`1", "TypeDefinition:$B865B3ED3C68CE2EBBC104FFAF3CFF93" ], reader.DumpNestedTypes(e.Handle)); } @@ -25957,7 +27069,7 @@ static void validate(ModuleSymbol module) var reader = ((PEModuleSymbol)module).Module.GetMetadataReader(); AssertEx.Equal([ "TypeDefinition:E", - "TypeDefinition:$8048A6C8BE30A622530249B904B537EB", + "TypeDefinition:$8048A6C8BE30A622530249B904B537EB`1", "TypeDefinition:$C7A07C3975E80DE5DBC93B5392C6C922", "TypeDefinition:$2789E59A55056F0AD9E820EBD5BCDFBF" ], reader.DumpNestedTypes(e.Handle)); @@ -26331,7 +27443,7 @@ static void validate(ModuleSymbol module) var reader = ((PEModuleSymbol)module).Module.GetMetadataReader(); AssertEx.Equal([ "TypeDefinition:E", - "TypeDefinition:$B8D310208B4544F25EEBACB9990FC73B", + "TypeDefinition:$B8D310208B4544F25EEBACB9990FC73B`1", "TypeDefinition:$F167169D271C76FCF9FF858EA5CFC454", "TypeDefinition:$9D7BB308433678477E9C2F4392A27B18" ], reader.DumpNestedTypes(e.Handle)); @@ -26368,7 +27480,7 @@ static void validate(ModuleSymbol module) var reader = ((PEModuleSymbol)module).Module.GetMetadataReader(); AssertEx.Equal([ "TypeDefinition:E", - "TypeDefinition:$8048A6C8BE30A622530249B904B537EB", + "TypeDefinition:$8048A6C8BE30A622530249B904B537EB`1", "TypeDefinition:$C7A07C3975E80DE5DBC93B5392C6C922", "TypeDefinition:$01CE3801593377B4E240F33E20D30D50" ], reader.DumpNestedTypes(e.Handle)); @@ -26403,7 +27515,7 @@ static void validate(ModuleSymbol module) var reader = ((PEModuleSymbol)module).Module.GetMetadataReader(); AssertEx.Equal([ "TypeDefinition:E", - "TypeDefinition:$B8D310208B4544F25EEBACB9990FC73B", + "TypeDefinition:$B8D310208B4544F25EEBACB9990FC73B`1", "TypeDefinition:$A189EAA0A09C2534B53DBF86166AD56A", "TypeDefinition:$869530FF3C2454D7BCCC5A8D0E31052F" ], reader.DumpNestedTypes(e.Handle)); @@ -26438,7 +27550,7 @@ static void validate(ModuleSymbol module) var reader = ((PEModuleSymbol)module).Module.GetMetadataReader(); AssertEx.Equal([ "TypeDefinition:E", - "TypeDefinition:$B8D310208B4544F25EEBACB9990FC73B", + "TypeDefinition:$B8D310208B4544F25EEBACB9990FC73B`1", "TypeDefinition:$9D7BB308433678477E9C2F4392A27B18" ], reader.DumpNestedTypes(e.Handle)); } @@ -27982,6 +29094,218 @@ public interface I { } VerifyCollisions(comp, groupingMatch: true, markerMatch: true); } + [Fact] + public void ExportedTypes_01() + { + string source = @" +public static class C1 +{ + extension(int i) + { + } + + extension(string s) + { + } + + extension(ref int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var moduleComp = CreateCompilation(source, options: TestOptions.ReleaseModule); + var moduleRef = moduleComp.EmitToImageReference(); + + CompileAndVerify("", references: [moduleRef], assemblyValidator: (assembly) => + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + ".C1 0x26000001 (AssemblyFile) 0x0001", + ".C2 0x27000001 (ExportedType) 0x0002", + ".C3 0x27000002 (ExportedType) 0x0002", + ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0002", + ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000004 (ExportedType) 0x0002", + ".$56B5C634B2E52051C75D91F71BA8833A 0x27000004 (ExportedType) 0x0002", + ".$34505F560D9EACF86A87F3ED1F85E448 0x27000001 (ExportedType) 0x0002", + ".$69A44968D4F2B90D6BA4472A51F540A4 0x27000007 (ExportedType) 0x0002", + ], actual); + }); + } + + [Fact] + public void ExportedTypes_02() + { + string source = @" +namespace N1.N2; + +public static class C1 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var moduleComp = CreateCompilation(source, options: TestOptions.ReleaseModule); + var moduleRef = moduleComp.EmitToImageReference(); + + CompileAndVerify("", references: [moduleRef], assemblyValidator: (assembly) => + { + var reader = assembly.GetMetadataReader(); + + var actual = from h in reader.ExportedTypes + let et = reader.GetExportedType(h) + select $"{reader.GetString(et.NamespaceDefinition)}.{reader.GetString(et.Name)} 0x{MetadataTokens.GetToken(et.Implementation):X8} ({et.Implementation.Kind}) 0x{(int)et.Attributes:X4}"; + + AssertEx.Equal( + [ + "N1.N2.C1 0x26000001 (AssemblyFile) 0x0001", + ".C2 0x27000001 (ExportedType) 0x0002", + ".C3 0x27000002 (ExportedType) 0x0002", + ".$BA41CFE2B5EDAEB8C1B9062F59ED4D69 0x27000001 (ExportedType) 0x0002", + ".$F4B4FFE41AB49E80A4ECF390CF6EB372 0x27000004 (ExportedType) 0x0002", + ], actual); + }); + } + + [Fact] + public void ExportedTypes_03() + { + string moduleSource = @" +public static class C1 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} + +internal static class C4 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var moduleComp = CreateCompilation(moduleSource, options: TestOptions.ReleaseModule); + var moduleRef = moduleComp.EmitToImageReference(); + + string source = @" +static class C1 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} + +static class C4 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + + CreateCompilation(source, references: [moduleRef]).VerifyEmitDiagnostics( + // (2,14): error CS0101: The namespace '' already contains a definition for 'C1' + // static class C1 + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C1").WithArguments("C1", "").WithLocation(2, 14), + // (14,14): error CS0101: The namespace '' already contains a definition for 'C4' + // static class C4 + Diagnostic(ErrorCode.ERR_DuplicateNameInNS, "C4").WithArguments("C4", "").WithLocation(14, 14) + ); + } + + [Fact] + public void ExportedTypes_04() + { + string moduleSource = @" +public static class C1 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var module1Comp = CreateCompilation(moduleSource, options: TestOptions.ReleaseModule); + var module1Ref = module1Comp.EmitToImageReference(); + + var module2Comp = CreateCompilation(moduleSource, options: TestOptions.ReleaseModule); + var module2Ref = module2Comp.EmitToImageReference(); + + CreateCompilation("", references: [module1Ref, module2Ref]).VerifyEmitDiagnostics( + // error CS0101: The namespace '' already contains a definition for 'C1' + Diagnostic(ErrorCode.ERR_DuplicateNameInNS).WithArguments("C1", "").WithLocation(1, 1) + ); + } + + [Fact] + public void ExportedTypes_05() + { + string moduleSource = @" +internal static class C1 +{ + extension(int i) + { + } + + public class C2 + { + public class C3; + } +} +"; + var module1Comp = CreateCompilation(moduleSource, options: TestOptions.ReleaseModule); + var module1Ref = module1Comp.EmitToImageReference(); + + var module2Comp = CreateCompilation(moduleSource, options: TestOptions.ReleaseModule); + var module2Ref = module2Comp.EmitToImageReference(); + + CreateCompilation("", references: [module1Ref, module2Ref]).VerifyEmitDiagnostics( + // error CS0101: The namespace '' already contains a definition for 'C1' + Diagnostic(ErrorCode.ERR_DuplicateNameInNS).WithArguments("C1", "").WithLocation(1, 1) + ); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79193")] public void OverloadResolution_01() { @@ -30140,7 +31464,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$B8D310208B4544F25EEBACB9990FC73B'<$T0> + .class nested public auto ansi sealed specialname '$B8D310208B4544F25EEBACB9990FC73B`1'<$T0> extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -30155,10 +31479,10 @@ .param type T 01 00 00 00 ) // Methods - .method public hidebysig specialname static + .method public hidebysig specialname static void '$' ( int32 i - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 @@ -30170,8 +31494,8 @@ .maxstack 8 } // end of method '$73F5560BE55A0A0B23905153DB511F4E'::'$' } // end of class $73F5560BE55A0A0B23905153DB511F4E // Methods - .method public hidebysig - instance void M () cil managed + .method public hidebysig + instance void M () cil managed { .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( 01 00 24 3c 4d 3e 24 37 33 46 35 35 36 30 42 45 @@ -30183,13 +31507,13 @@ 42 35 31 31 46 34 45 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$B8D310208B4544F25EEBACB9990FC73B'::M - } // end of class $B8D310208B4544F25EEBACB9990FC73B + } // end of method '$B8D310208B4544F25EEBACB9990FC73B`1'::M + } // end of class $B8D310208B4544F25EEBACB9990FC73B`1 // Methods - .method public hidebysig static + .method public hidebysig static void M ( int32 i - ) cil managed + ) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 @@ -30640,7 +31964,7 @@ extends [mscorlib]System.Object 01 00 00 00 ) // Nested Types - .class nested public auto ansi sealed specialname '$8A1E908054B5C3DCE56554F1F294FA98' + .class nested public auto ansi sealed specialname '$8A1E908054B5C3DCE56554F1F294FA98`1' extends [mscorlib]System.Object { .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( @@ -30687,8 +32011,8 @@ 35 34 37 32 43 43 38 00 00 .maxstack 8 IL_0000: ldnull IL_0001: throw - } // end of method '$8A1E908054B5C3DCE56554F1F294FA98'::M - } // end of class $8A1E908054B5C3DCE56554F1F294FA98 + } // end of method '$8A1E908054B5C3DCE56554F1F294FA98`1'::M + } // end of class $8A1E908054B5C3DCE56554F1F294FA98`1 // Methods .method public hidebysig static void M ( @@ -31155,5 +32479,449 @@ .maxstack 8 } // end of class E """.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } + + [Fact] + public void AssociatedExtensionImplementation_01() + { + var src = """ +public static class E +{ + extension(int i) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var extensionMethod = comp.GlobalNamespace.GetTypeMember("E").GetTypeMember("").GetMember("M").GetPublicSymbol(); + Assert.Equal("void E.M(this System.Int32 i)", extensionMethod.AssociatedExtensionImplementation.ToTestDisplayString()); + } + + [Fact] + public void AssociatedExtensionImplementation_02() + { + // not a definition + var src = """ +42.M(); + +public static class E +{ + extension(T t) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + var method = (IMethodSymbol)model.GetSymbolInfo(memberAccess).Symbol; + // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, add support for constructed symbols + Assert.Null(method.AssociatedExtensionImplementation); + Assert.Equal("void E.M(this T t)", method.OriginalDefinition.AssociatedExtensionImplementation.ToTestDisplayString()); + } + + [Fact] + public void AssociatedExtensionImplementation_03() + { + // not an extension method + var src = """ +public class E +{ + public void M() { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var method = comp.GlobalNamespace.GetTypeMember("E").GetMember("M").GetPublicSymbol(); + Assert.Null(method.AssociatedExtensionImplementation); + } + + [Fact] + public void AssociatedExtensionImplementation_04() + { + // missing implementation method in metadata + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi abstract sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .method public hidebysig specialname static void '$' ( int32 '' ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + ret + } + } + .method public hidebysig static void M () cil managed + { + .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( + 01 00 24 3c 4d 3e 24 42 41 34 31 43 46 45 32 42 + 35 45 44 41 45 42 38 43 31 42 39 30 36 32 46 35 + 39 45 44 34 44 36 39 00 00 + ) + ldnull + throw + } + } +} +""" + ExtensionMarkerAttributeIL; + + var comp = CreateCompilationWithIL("", ilSrc); + var method = comp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Null(method.AssociatedExtensionImplementation); + } + + [Fact] + public void AssociatedExtensionImplementation_05() + { + // incorrect accessibility on implementation method in metadata + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi abstract sealed specialname '$BA41CFE2B5EDAEB8C1B9062F59ED4D69' + extends [mscorlib]System.Object + { + .method public hidebysig specialname static void '$' ( int32 '' ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + ret + } + } + .method public hidebysig static void M () cil managed + { + .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( + 01 00 24 3c 4d 3e 24 42 41 34 31 43 46 45 32 42 + 35 45 44 41 45 42 38 43 31 42 39 30 36 32 46 35 + 39 45 44 34 44 36 39 00 00 + ) + ldnull + throw + } + } + .method family hidebysig static void M () cil managed + { + ret + } +} +""" + ExtensionMarkerAttributeIL; + + var comp = CreateCompilationWithIL("", ilSrc); + var method = comp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Null(method.AssociatedExtensionImplementation); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78606")] + public void DocumentationCommentId_01() + { + var src = """ +public static class E +{ + extension(int i1) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + validate(comp); + + var compRef = comp.EmitToImageReference(); + var comp2 = CreateCompilation("", references: [compRef]); + validate(comp2); + + var vbComp = CreateVisualBasicCompilation("", referencedAssemblies: [compRef]); + var vbGroupingType = vbComp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single(); + Assert.False(vbGroupingType.IsExtension); + AssertEx.Equal("T:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69", vbGroupingType.GetDocumentationCommentId()); + var vbM = vbGroupingType.GetMember("M"); + AssertEx.Equal("M:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M", vbM.GetDocumentationCommentId()); + var vbMarkerType = vbGroupingType.GetTypeMembers().Single(); + Assert.False(vbMarkerType.IsExtension); + AssertEx.Equal("T:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$531E7AC45D443AE2243E7FFAB9455D60", + vbMarkerType.GetDocumentationCommentId()); + + static void validate(CSharpCompilation comp) + { + var e = comp.GlobalNamespace.GetTypeMember("E"); + Assert.False(e.IsExtension); + Assert.Null(e.ExtensionGroupingName); + Assert.Null(e.ExtensionMarkerName); + + // extension + var extension = e.GetTypeMembers().Single().GetPublicSymbol(); + Assert.True(extension.IsExtension); + AssertEx.Equal("E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$531E7AC45D443AE2243E7FFAB9455D60", DocumentationCommentId.CreateReferenceId(extension)); + AssertEx.Equal("T:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$531E7AC45D443AE2243E7FFAB9455D60", DocumentationCommentId.CreateDeclarationId(extension)); + Assert.Equal("$BA41CFE2B5EDAEB8C1B9062F59ED4D69", extension.ExtensionGroupingName); + Assert.Equal("$531E7AC45D443AE2243E7FFAB9455D60", extension.ExtensionMarkerName); + AssertEx.Equal("T:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$531E7AC45D443AE2243E7FFAB9455D60", extension.GetDocumentationCommentId()); + var found = (INamedTypeSymbol)DocumentationCommentId.GetSymbolsForDeclarationId(DocumentationCommentId.CreateDeclarationId(extension), comp).Single(); + Assert.True(found.IsExtension); + AssertEx.Equal("E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.$531E7AC45D443AE2243E7FFAB9455D60", found.ToTestDisplayString()); + + // extension member + var m = e.GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Equal("", DocumentationCommentId.CreateReferenceId(m)); + Assert.Equal("M:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M", m.GetDocumentationCommentId()); + + var declarationId = DocumentationCommentId.CreateDeclarationId(m); + AssertEx.Equal("M:E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M", declarationId); + AssertEx.Equal("void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M()", DocumentationCommentId.GetFirstSymbolForDeclarationId(declarationId, comp).ToTestDisplayString()); + AssertEx.Equal(["void E.$BA41CFE2B5EDAEB8C1B9062F59ED4D69.M()"], DocumentationCommentId.GetSymbolsForDeclarationId(declarationId, comp).ToTestDisplayStrings()); + + // implementation method + var mImplementation = e.GetMember("M").GetPublicSymbol(); + declarationId = DocumentationCommentId.CreateDeclarationId(mImplementation); + AssertEx.Equal("M:E.M(System.Int32)", declarationId); + AssertEx.Equal("void E.M(this System.Int32 i1)", DocumentationCommentId.GetFirstSymbolForDeclarationId(declarationId, comp).ToTestDisplayString()); + AssertEx.Equal(["void E.M(this System.Int32 i1)"], DocumentationCommentId.GetSymbolsForDeclarationId(declarationId, comp).ToTestDisplayStrings()); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78606")] + public void DocumentationCommentId_02() + { + var src = """ +42.M(); + +public static class E +{ + extension(T t) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + validate(comp); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + var method = model.GetSymbolInfo(memberAccess).Symbol; + AssertEx.Equal("void E.$8048A6C8BE30A622530249B904B537EB.M()", method.ToTestDisplayString()); + AssertEx.Equal("", DocumentationCommentId.CreateReferenceId(method)); + AssertEx.Equal("M:E.$8048A6C8BE30A622530249B904B537EB`1.M", DocumentationCommentId.CreateDeclarationId(method)); + + var extension = method.ContainingType; + AssertEx.Equal("E.$8048A6C8BE30A622530249B904B537EB{System.Int32}.$D1693D81A12E8DED4ED68FE22D9E856F", + DocumentationCommentId.CreateReferenceId(extension)); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", + DocumentationCommentId.CreateDeclarationId(extension)); + + var compRef = comp.EmitToImageReference(); + var comp2 = CreateCompilation("", references: [compRef]); + validate(comp2); + + var vbComp = CreateVisualBasicCompilation("", referencedAssemblies: [compRef]); + var vbGroupingType = vbComp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single(); + Assert.False(vbGroupingType.IsExtension); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1", vbGroupingType.GetDocumentationCommentId()); + var vbM = vbGroupingType.GetMember("M"); + AssertEx.Equal("M:E.$8048A6C8BE30A622530249B904B537EB`1.M", vbM.GetDocumentationCommentId()); + var vbMarkerType = vbGroupingType.GetTypeMembers().Single(); + Assert.False(vbMarkerType.IsExtension); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", + vbMarkerType.GetDocumentationCommentId()); + + static void validate(CSharpCompilation comp) + { + var e = comp.GlobalNamespace.GetTypeMember("E"); + Assert.False(e.IsExtension); + Assert.Null(e.ExtensionGroupingName); + Assert.Null(e.ExtensionMarkerName); + + // extension + var extension = e.GetTypeMembers().Single().GetPublicSymbol(); + Assert.True(extension.IsExtension); + AssertEx.Equal("E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", DocumentationCommentId.CreateReferenceId(extension)); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", DocumentationCommentId.CreateDeclarationId(extension)); + Assert.Equal("$8048A6C8BE30A622530249B904B537EB", extension.ExtensionGroupingName); + Assert.Equal("$D1693D81A12E8DED4ED68FE22D9E856F", extension.ExtensionMarkerName); + AssertEx.Equal("T:E.$8048A6C8BE30A622530249B904B537EB`1.$D1693D81A12E8DED4ED68FE22D9E856F", extension.GetDocumentationCommentId()); + var found = (INamedTypeSymbol)DocumentationCommentId.GetSymbolsForDeclarationId(DocumentationCommentId.CreateDeclarationId(extension), comp).Single(); + Assert.True(found.IsExtension); + AssertEx.Equal("E.$8048A6C8BE30A622530249B904B537EB.$D1693D81A12E8DED4ED68FE22D9E856F", found.ToTestDisplayString()); + + AssertEx.Equal("E+$8048A6C8BE30A622530249B904B537EB+$D1693D81A12E8DED4ED68FE22D9E856F", + found.ToDisplayString(SymbolDisplayFormat.TestFormat.WithCompilerInternalOptions(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames | SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes))); + + // extension member + var m = e.GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Equal("", DocumentationCommentId.CreateReferenceId(m)); + Assert.Equal("M:E.$8048A6C8BE30A622530249B904B537EB`1.M", m.GetDocumentationCommentId()); + + var declarationId = DocumentationCommentId.CreateDeclarationId(m); + AssertEx.Equal("M:E.$8048A6C8BE30A622530249B904B537EB`1.M", declarationId); + AssertEx.Equal("void E.$8048A6C8BE30A622530249B904B537EB.M()", DocumentationCommentId.GetFirstSymbolForDeclarationId(declarationId, comp).ToTestDisplayString()); + AssertEx.Equal(["void E.$8048A6C8BE30A622530249B904B537EB.M()"], DocumentationCommentId.GetSymbolsForDeclarationId(declarationId, comp).ToTestDisplayStrings()); + + // implementation method + var mImplementation = e.GetMember("M").GetPublicSymbol(); + declarationId = DocumentationCommentId.CreateDeclarationId(mImplementation); + AssertEx.Equal("M:E.M``1(``0)", declarationId); + AssertEx.Equal("void E.M(this T t)", DocumentationCommentId.GetFirstSymbolForDeclarationId(declarationId, comp).ToTestDisplayString()); + AssertEx.Equal(["void E.M(this T t)"], DocumentationCommentId.GetSymbolsForDeclarationId(declarationId, comp).ToTestDisplayStrings()); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78606")] + public void DocumentationCommentId_03() + { + // grouping type name from metadata without arity suffix + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi sealed specialname 'GroupingType'<$T0> + extends System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi abstract sealed specialname 'MarkerType' + extends System.Object + { + .method public hidebysig specialname static void '$' ( !T t ) cil managed + { + ret + } + } + .method public hidebysig instance void M () cil managed + { + .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( + 01 00 0a 4d 61 72 6b 65 72 54 79 70 65 00 00 + ) + ldnull + throw + } + } + .method public hidebysig static void M ( !!T t ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + ldarg.0 + box !!T + call void [mscorlib]System.Console::Write(object) + ret + } +} +""" + ExtensionMarkerAttributeIL; + + var src = """ +42.M(); +"""; + var comp = CreateCompilationWithIL(src, ilSrc); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + validate(comp); + + MetadataReference ilReference = CompileIL(ilSrc); + var allReferences = TargetFrameworkUtil.GetReferences(targetFramework: TargetFramework.Standard).Add(ilReference); + var vbComp = CreateVisualBasicCompilation("", referencedAssemblies: allReferences); + var vbGroupingType = vbComp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single(); + Assert.False(vbGroupingType.IsExtension); + AssertEx.Equal("T:E.GroupingType`1", vbGroupingType.GetDocumentationCommentId()); + var vbM = vbGroupingType.GetMember("M"); + AssertEx.Equal("M:E.GroupingType`1.M", vbM.GetDocumentationCommentId()); + var vbMarkerType = vbGroupingType.GetTypeMembers().Single(); + Assert.False(vbMarkerType.IsExtension); + AssertEx.Equal("T:E.GroupingType`1.MarkerType", vbMarkerType.GetDocumentationCommentId()); + + static void validate(CSharpCompilation comp) + { + var e = comp.GlobalNamespace.GetTypeMember("E"); + Assert.False(e.IsExtension); + Assert.Null(e.ExtensionGroupingName); + Assert.Null(e.ExtensionMarkerName); + + // extension + var extension = e.GetTypeMembers().Single().GetPublicSymbol(); + Assert.True(extension.IsExtension); + AssertEx.Equal("E.GroupingType`1.MarkerType", DocumentationCommentId.CreateReferenceId(extension)); + AssertEx.Equal("T:E.GroupingType`1.MarkerType", DocumentationCommentId.CreateDeclarationId(extension)); + Assert.Equal("GroupingType", extension.ExtensionGroupingName); + Assert.Equal("MarkerType", extension.ExtensionMarkerName); + AssertEx.Equal("T:E.GroupingType`1.MarkerType", extension.GetDocumentationCommentId()); + var found = (INamedTypeSymbol)DocumentationCommentId.GetSymbolsForDeclarationId(DocumentationCommentId.CreateDeclarationId(extension), comp).Single(); + Assert.True(found.IsExtension); + AssertEx.Equal("E.GroupingType.MarkerType", found.ToTestDisplayString()); + + // extension member + var m = e.GetTypeMembers().Single().GetMember("M").GetPublicSymbol(); + Assert.Equal("", DocumentationCommentId.CreateReferenceId(m)); + Assert.Equal("M:E.GroupingType`1.M", m.GetDocumentationCommentId()); + + var declarationId = DocumentationCommentId.CreateDeclarationId(m); + AssertEx.Equal("M:E.GroupingType`1.M", declarationId); + AssertEx.Equal("void E.GroupingType.M()", DocumentationCommentId.GetFirstSymbolForDeclarationId(declarationId, comp).ToTestDisplayString()); + AssertEx.Equal(["void E.GroupingType.M()"], DocumentationCommentId.GetSymbolsForDeclarationId(declarationId, comp).ToTestDisplayStrings()); + + // implementation method + var mImplementation = e.GetMember("M").GetPublicSymbol(); + declarationId = DocumentationCommentId.CreateDeclarationId(mImplementation); + AssertEx.Equal("M:E.M``1(``0)", declarationId); + AssertEx.Equal("void E.M(this T t)", DocumentationCommentId.GetFirstSymbolForDeclarationId(declarationId, comp).ToTestDisplayString()); + AssertEx.Equal(["void E.M(this T t)"], DocumentationCommentId.GetSymbolsForDeclarationId(declarationId, comp).ToTestDisplayStrings()); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78606")] + public void DocumentationCommentId_04() + { + // grouping and marker type names from metadata are VB-escapable + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi sealed specialname 'Rem' + extends System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .class nested public auto ansi abstract sealed specialname 'New' + extends System.Object + { + .method public hidebysig specialname static void '$' ( object o ) cil managed + { + ret + } + } + .method public hidebysig instance void M () cil managed + { + .custom instance void System.Runtime.CompilerServices.ExtensionMarkerAttribute::.ctor(string) = ( + 01 00 03 4e 65 77 00 00 + ) + ldnull + throw + } + } + .method public hidebysig static void M ( object o ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + ret + } +} +""" + ExtensionMarkerAttributeIL; + + var comp = CreateCompilationWithIL("", ilSrc); + var extension = comp.GlobalNamespace.GetTypeMember("E").GetTypeMembers().Single().GetPublicSymbol(); + Assert.True(extension.IsExtension); + Assert.Equal("E.[Rem].[New]", VisualBasic.SymbolDisplay.ToDisplayString(extension)); + } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs index 8420d2ce0fd..b2e69217442 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs @@ -19491,8 +19491,73 @@ public T Current } } "; + var expectedOutput = " 111 112 113 114"; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: " 111 112 113 114").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.FailsILVerify with + { + ILVerifyMessage = """ + [Main]: Return value missing on the stack. { Offset = 0x7f } + [MoveNextAsync]: Unexpected type on the stack. { Offset = 0x2f, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' } + """ + }); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Main()", """ + { + // Code size 128 (0x80) + .maxstack 2 + .locals init (C V_0, //x + Buffer4.AsyncEnumerator V_1, + System.Threading.CancellationToken V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj "C" + IL_0008: ldloca.s V_0 + IL_000a: ldflda "Buffer4 C.F" + IL_000f: call "ref int .InlineArrayFirstElementRef, int>(ref Buffer4)" + IL_0014: ldc.i4.s 111 + IL_0016: stind.i4 + IL_0017: ldloca.s V_0 + IL_0019: ldflda "Buffer4 C.F" + IL_001e: ldc.i4.1 + IL_001f: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0024: ldc.i4.s 112 + IL_0026: stind.i4 + IL_0027: ldloca.s V_0 + IL_0029: ldflda "Buffer4 C.F" + IL_002e: ldc.i4.2 + IL_002f: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0034: ldc.i4.s 113 + IL_0036: stind.i4 + IL_0037: ldloca.s V_0 + IL_0039: ldflda "Buffer4 C.F" + IL_003e: ldc.i4.3 + IL_003f: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0044: ldc.i4.s 114 + IL_0046: stind.i4 + IL_0047: ldloca.s V_0 + IL_0049: ldflda "Buffer4 C.F" + IL_004e: ldloca.s V_2 + IL_0050: initobj "System.Threading.CancellationToken" + IL_0056: ldloc.2 + IL_0057: call "Buffer4.AsyncEnumerator Buffer4.GetAsyncEnumerator(System.Threading.CancellationToken)" + IL_005c: stloc.1 + IL_005d: br.s IL_0071 + IL_005f: ldloc.1 + IL_0060: callvirt "int Buffer4.AsyncEnumerator.Current.get" + IL_0065: ldc.i4.s 32 + IL_0067: call "void System.Console.Write(char)" + IL_006c: call "void System.Console.Write(int)" + IL_0071: ldloc.1 + IL_0072: ldc.i4.1 + IL_0073: callvirt "System.Threading.Tasks.Task Buffer4.AsyncEnumerator.MoveNextAsync(int)" + IL_0078: call "bool System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_007d: brtrue.s IL_005f + IL_007f: ret + } + """); } [Fact] @@ -20171,8 +20236,9 @@ static void Increment() } } "; + var expectedOutput = " 0 1 2 3"; var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); verifier.VerifyIL("Program.d__3.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" @@ -20323,7 +20389,67 @@ .locals init (int V_0, } "); comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(src + Buffer4Definition); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = """ + [Test]: Return value missing on the stack. { Offset = 0x62 } + [InlineArrayAsSpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0xc } + """ + }); + verifier.VerifyIL("Program.Test(C)", """ + { + // Code size 99 (0x63) + .maxstack 2 + .locals init (C V_0, + int V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3) + IL_0000: ldarg.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldfld "Buffer4 C.F" + IL_0008: pop + IL_0009: ldc.i4.0 + IL_000a: stloc.1 + IL_000b: br.s IL_005e + IL_000d: ldloc.0 + IL_000e: ldflda "Buffer4 C.F" + IL_0013: ldloc.1 + IL_0014: call "ref int .InlineArrayElementRef, int>(ref Buffer4, int)" + IL_0019: ldind.i4 + IL_001a: call "void Program.Increment()" + IL_001f: ldc.i4.s 32 + IL_0021: call "void System.Console.Write(char)" + IL_0026: call "void System.Console.Write(int)" + IL_002b: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0030: stloc.3 + IL_0031: ldloca.s V_3 + IL_0033: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0038: stloc.2 + IL_0039: ldloca.s V_2 + IL_003b: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0040: brtrue.s IL_0048 + IL_0042: ldloc.2 + IL_0043: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0048: ldloca.s V_2 + IL_004a: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_004f: ldc.i4.2 + IL_0050: call "System.Threading.Tasks.Task System.Threading.Tasks.Task.Delay(int)" + IL_0055: call "void System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_005a: ldloc.1 + IL_005b: ldc.i4.1 + IL_005c: add + IL_005d: stloc.1 + IL_005e: ldloc.1 + IL_005f: ldc.i4.4 + IL_0060: blt.s IL_000d + IL_0062: ret + } + """); } [ConditionalFact(typeof(CoreClrOnly))] @@ -20384,17 +20510,20 @@ static async void Test() 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, + // (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()) + DiagnosticDescription expected = Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (int y in GetBuffer()) { await System.Threading.Tasks.Task.Yield(); - }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) - ); + }").WithArguments("Program.GetBuffer()").WithLocation(6, 9); + + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics(expected); + + comp = CreateRuntimeAsyncCompilation(src + Buffer4Definition); + comp.VerifyEmitDiagnostics(expected); } [ConditionalFact(typeof(CoreClrOnly))] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/LockTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/LockTests.cs index 192bbd51c8f..570e26e36d8 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/LockTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/LockTests.cs @@ -1926,7 +1926,6 @@ public void Await() public void AsyncMethod() { var source = """ - #pragma warning disable 1998 // async method lacks 'await' operators using System.Threading; using System.Threading.Tasks; @@ -2373,7 +2372,6 @@ .locals init (int V_0, public void AsyncMethod_AwaitResource() { var source = """ - #pragma warning disable 1998 // async method lacks 'await' operators using System.Threading; using System.Threading.Tasks; @@ -2597,7 +2595,6 @@ .locals init (int V_0, public void AsyncLocalFunction() { var source = """ - #pragma warning disable 1998 // async method lacks 'await' operators using System.Threading; async void local() @@ -3041,7 +3038,6 @@ .locals init (int V_0, public void AsyncLambda() { var source = """ - #pragma warning disable 1998 // async method lacks 'await' operators using System.Threading; var lam = async () => diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs index 36a1708cfa7..544d66386b6 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs @@ -652,8 +652,7 @@ static object Test1(out int x) }"; var compilation = CreateCompilation(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); @@ -769,8 +768,7 @@ static object Test1(out int x) }"; var compilation = CreateCompilation(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "123"); Assert.False(compilation.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().Any()); @@ -19580,11 +19578,8 @@ static void Test2(object x, System.ArgIterator y) compilation.VerifyDiagnostics( // (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), - // (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) - ); + Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out System.ArgIterator x").WithArguments("System.ArgIterator").WithLocation(11, 25) + ); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -19635,11 +19630,8 @@ static void Test2(object x, System.ArgIterator y) Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion12, "var").WithArguments("ref and unsafe in async and iterator methods", "13.0").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), - // (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) - ); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(9, 13) + ); compilation = CreateCompilation(text, targetFramework: TargetFramework.Mscorlib461); @@ -19649,11 +19641,8 @@ static void Test2(object x, System.ArgIterator y) Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out System.ArgIterator x").WithArguments("System.ArgIterator").WithLocation(12, 25), // (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), - // (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) - ); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(9, 13) + ); var tree = compilation.SyntaxTrees.Single(); var model = compilation.GetSemanticModel(tree); @@ -35732,8 +35721,7 @@ static void DumpArgs(ArgIterator args) }"; var compilation = CreateCompilation(text, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); CompileAndVerify(compilation, expectedOutput: "23True"); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs index 2a80d5a0d19..608dbdaf139 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs @@ -20,15 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics { public class ParamsCollectionTests : CompilingTestBase { - private const string ParamCollectionAttributeSource = @" -namespace System.Runtime.CompilerServices -{ - public sealed class ParamCollectionAttribute : Attribute - { - public ParamCollectionAttribute() { } - } -} -"; + private static string ParamCollectionAttributeSource => TestSources.ParamsCollectionAttribute; private static void VerifyParamsAndAttribute(ParameterSymbol parameter, bool isParamArray = false, bool isParamCollection = false) { @@ -2741,10 +2733,8 @@ static void Main() symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); - AssertEx.Equal("void Program.<>c.
b__0_0(System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); - - Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); + AssertEx.Equal("void Program.<>c.
b__0_0(params System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); }).VerifyDiagnostics( // (7,72): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. // System.Action> l = (params IEnumerable x) => {}; @@ -2797,8 +2787,8 @@ static void Main() symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); - AssertEx.Equal("void Program.<>c.
b__0_0(System.Int64[] x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + AssertEx.Equal("void Program.<>c.
b__0_0(params System.Int64[] x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last(), isParamArray: true); }).VerifyDiagnostics( // (5,50): warning CS9100: Parameter 1 has params modifier in lambda but not in target delegate type. // System.Action l = (params long[] x) => {}; @@ -2943,10 +2933,8 @@ static void Main() symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__local|0_0"); - AssertEx.Equal("void Program.
g__local|0_0(System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); - - Assert.Empty(((NamespaceSymbol)m.GlobalNamespace.GetMember("System.Runtime.CompilerServices")).GetMembers("ParamCollectionAttribute")); + AssertEx.Equal("void Program.
g__local|0_0(params System.Collections.Generic.IEnumerable x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); }).VerifyDiagnostics(); var tree = comp.SyntaxTrees.Single(); @@ -2992,8 +2980,8 @@ static void Main() symbolValidator: (m) => { MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__local|0_0"); - AssertEx.Equal("void Program.
g__local|0_0(System.Int64[] x)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + AssertEx.Equal("void Program.
g__local|0_0(params System.Int64[] x)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last(), isParamArray: true); }).VerifyDiagnostics(); } @@ -3078,7 +3066,7 @@ .locals init (<>y__InlineArray2 V_0, IL_006c: ldloca.s V_0 IL_006e: ldc.i4.2 IL_006f: call ""System.ReadOnlySpan .InlineArrayAsReadOnlySpan<<>y__InlineArray2, CustomHandler>(in <>y__InlineArray2, int)"" - IL_0074: call ""void Program.<
$>g__M|0_0(scoped System.ReadOnlySpan)"" + IL_0074: call ""void Program.<
$>g__M|0_0(params System.ReadOnlySpan)"" IL_0079: ret } "); @@ -4533,15 +4521,13 @@ void verify(CSharpCompilation comp, bool attributeIsEmbedded) AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); - // Note, no attributes on lambdas - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_0"); - AssertEx.Equal("void Program.<>c.
b__0_0(scoped System.ReadOnlySpan a)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + AssertEx.Equal("void Program.<>c.
b__0_0(params System.ReadOnlySpan a)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); MethodSymbol l2 = m.GlobalNamespace.GetMember("Program.<>c.
b__0_1"); - AssertEx.Equal("void Program.<>c.
b__0_1(System.Int64[] a)", l2.ToTestDisplayString()); - VerifyParamsAndAttribute(l2.Parameters.Last()); + AssertEx.Equal("void Program.<>c.
b__0_1(params System.Int64[] a)", l2.ToTestDisplayString()); + VerifyParamsAndAttribute(l2.Parameters.Last(), isParamArray: true); if (attributeIsEmbedded) { @@ -4686,15 +4672,13 @@ void verify(CSharpCompilation comp, bool attributeIsEmbedded) AssertEx.Equal("void <>f__AnonymousDelegate1.Invoke(params T1[] arg)", delegateInvokeMethod2.ToTestDisplayString()); VerifyParamsAndAttribute(delegateInvokeMethod2.Parameters.Last(), isParamArray: true); - // Note, no attributes on local functions - MethodSymbol l1 = m.GlobalNamespace.GetMember("Program.
g__Test1|0_0"); - AssertEx.Equal("void Program.
g__Test1|0_0(scoped System.ReadOnlySpan a)", l1.ToTestDisplayString()); - VerifyParamsAndAttribute(l1.Parameters.Last()); + AssertEx.Equal("void Program.
g__Test1|0_0(params System.ReadOnlySpan a)", l1.ToTestDisplayString()); + VerifyParamsAndAttribute(l1.Parameters.Last(), isParamCollection: true); MethodSymbol l2 = m.GlobalNamespace.GetMember("Program.
g__Test2|0_1"); - AssertEx.Equal("void Program.
g__Test2|0_1(System.Int64[] a)", l2.ToTestDisplayString()); - VerifyParamsAndAttribute(l2.Parameters.Last()); + AssertEx.Equal("void Program.
g__Test2|0_1(params System.Int64[] a)", l2.ToTestDisplayString()); + VerifyParamsAndAttribute(l2.Parameters.Last(), isParamArray: true); if (attributeIsEmbedded) { @@ -12970,9 +12954,9 @@ void Test1(params IEnumerable a) { } src, "<>f__AnonymousDelegate0.Invoke", "void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", - // (7,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported - // var x1 = Test1; - Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "Test1").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(7, 18) + // (11,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // void Test1(params IEnumerable a) { } + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IEnumerable a").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(11, 20) ); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/PatternSwitchTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/PatternSwitchTests.cs index 8524ef1dff7..0b5eea35894 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/PatternSwitchTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Semantics/PatternSwitchTests.cs @@ -325,8 +325,7 @@ public static void Main() } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); } [Fact] @@ -647,8 +646,7 @@ public static void Main(string[] args) } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); } [Fact] @@ -667,8 +665,7 @@ public static void Main(string[] args) } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); } [Fact] @@ -952,8 +949,7 @@ public static void Main() } }"; var compilation = CreateCompilation(source, options: TestOptions.DebugExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); } [Fact] @@ -2661,11 +2657,7 @@ public static async Task SendMessageAsync(object response) "; // Use a compilation profile that supports Task. var compilation = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - // (12,38): 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. - // public static async Task SendMessageAsync(object response) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "SendMessageAsync").WithLocation(12, 38) - ); + compilation.VerifyDiagnostics(); var comp = CompileAndVerify(compilation, expectedOutput: @"T default"); @@ -2807,8 +2799,7 @@ static void Main() } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); var comp = CompileAndVerify(compilation, expectedOutput: @"pass"); } @@ -2835,8 +2826,7 @@ static void Main() } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); var comp = CompileAndVerify(compilation, expectedOutput: @"pass"); } @@ -2863,8 +2853,7 @@ static void Main() } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); var comp = CompileAndVerify(compilation, expectedOutput: @"1"); } @@ -2954,8 +2943,7 @@ static void Main() } "; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe); - compilation.VerifyDiagnostics( - ); + compilation.VerifyDiagnostics(); var comp = CompileAndVerify(compilation, expectedOutput: @"pass"); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs b/src/roslyn/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs index b6701cc8eef..d5718fa3185 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs @@ -5866,7 +5866,6 @@ static async Task Main(System.Collections.Generic.IAsyncEnumerable pets) public void AsyncForeach_StructEnumerator() { var compilation = CreateCompilation(@" -#pragma warning disable CS1998 // async method lacks awaits using System.Threading.Tasks; class C { @@ -5990,7 +5989,6 @@ public struct AsyncEnumerator public void AsyncForeach_StructEnumerator_ExplicitAsyncDisposeInterface() { var compilation = CreateCompilation(@" -#pragma warning disable CS1998 // async method lacks awaits using System.Threading.Tasks; class C { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs index 72a31e7fc05..654d83aa8fb 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs @@ -386,12 +386,6 @@ static int Main() "; var comp = CreateCompilationWithMscorlib461(text, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( - // (16,62): 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. - // dynamic f = (await GetVal((Func>)(async () => 1)))(); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(16, 62), - // (20,69): 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. - // dynamic ff = new Func>((Func>)(async () => 1)); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(20, 69), // (17,13): error CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create' // if (await f == 1) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "await f").WithArguments("Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo", "Create")); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs index 28ab05cc795..18bba00d45b 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs @@ -394,10 +394,8 @@ public void Private() using System.Runtime.CompilerServices; class C { -#pragma warning disable CS1998 static async MyTask F() { } static async MyTask G() { return 3; } -#pragma warning restore CS1998 [AsyncMethodBuilder(typeof(MyTaskMethodBuilder))] private class MyTask { } [AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))] @@ -455,11 +453,9 @@ static void F(Func> f) { } static void F(Func> f) { } static void M() { -#pragma warning disable CS1998 F(async () => { }); F(async () => { return 3; }); F(async () => { return string.Empty; }); -#pragma warning restore CS1998 } } [AsyncMethodBuilder(typeof(MyTaskMethodBuilder))] @@ -533,12 +529,10 @@ class C { static async void M() { -#pragma warning disable CS1998 async MyTask F() { } async MyTask G(T t) => t; await F(); await G(3); -#pragma warning restore CS1998 } } [AsyncMethodBuilder(typeof(MyTaskMethodBuilder))] @@ -614,11 +608,9 @@ static void F(Func> f) { } static void G(Func> f) { } static void M(object o) { -#pragma warning disable CS1998 F(async () => (dynamic)o); F(async () => new[] { (dynamic)o }); G(async () => o); -#pragma warning restore CS1998 } } [AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))] @@ -696,10 +688,8 @@ class C { static async void M() { -#pragma warning disable CS1998 async MyTask F() { }; await F(); -#pragma warning restore CS1998 } } [AsyncMethodBuilder(typeof(string))] @@ -718,12 +708,13 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : "; var compilation = CreateCompilationWithMscorlib461(source); compilation.VerifyEmitDiagnostics( - // (8,26): error CS0656: Missing compiler required member 'string.Task' + // (7,26): error CS0656: Missing compiler required member 'string.Task' // async MyTask F() { }; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ }").WithArguments("string", "Task").WithLocation(8, 26), - // (8,26): error CS0656: Missing compiler required member 'string.Create' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ }").WithArguments("string", "Task").WithLocation(7, 26), + // (7,26): error CS0656: Missing compiler required member 'string.Create' // async MyTask F() { }; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ }").WithArguments("string", "Create").WithLocation(8, 26)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "{ }").WithArguments("string", "Create").WithLocation(7, 26) + ); } [Fact] @@ -736,10 +727,8 @@ class C { static async void M() { -#pragma warning disable CS1998 async MyTask F(T t) => t; await F(3); -#pragma warning restore CS1998 } } [AsyncMethodBuilder(typeof(IEquatable<>))] @@ -758,12 +747,13 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : "; var compilation = CreateCompilationWithMscorlib461(source); compilation.VerifyEmitDiagnostics( - // (8,35): error CS0656: Missing compiler required member 'IEquatable.Task' + // (7,35): error CS0656: Missing compiler required member 'IEquatable.Task' // async MyTask F(T t) => t; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> t").WithArguments("System.IEquatable", "Task").WithLocation(8, 35), - // (8,35): error CS0656: Missing compiler required member 'IEquatable.Create' + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> t").WithArguments("System.IEquatable", "Task").WithLocation(7, 35), + // (7,35): error CS0656: Missing compiler required member 'IEquatable.Create' // async MyTask F(T t) => t; - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> t").WithArguments("System.IEquatable", "Create").WithLocation(8, 35)); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> t").WithArguments("System.IEquatable", "Create").WithLocation(7, 35) + ); } [Fact] @@ -776,10 +766,8 @@ class C { static async void M() { -#pragma warning disable CS1998 async MyTask F() { }; await F(); -#pragma warning restore CS1998 } } [AsyncMethodBuilder(typeof(object[]))] @@ -798,9 +786,9 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : "; var compilation = CreateCompilationWithMscorlib461(source); compilation.VerifyEmitDiagnostics( - // (8,22): error CS1983: The return type of an async method must be void, Task or Task + // (7,22): error CS1983: The return type of an async method must be void, Task or Task // async MyTask F() { }; - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "{ }").WithLocation(8, 26)); + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "{ }").WithLocation(7, 26)); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeTests.cs index d34a0870798..75440e3f1ae 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeTests.cs @@ -107,7 +107,6 @@ private bool VerifyTaskOverloads(string arg, string betterOverload, string worse using System.Threading.Tasks; class Program { - #pragma warning disable CS1998 static void Main() { var s = (<>); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index dd17c9c7c4d..a620978925a 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -595,10 +595,7 @@ async static Task F() { } }"; - CreateCompilationWithMscorlib461(source).VerifyDiagnostics( - // (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. - // async static Task F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F")); + CreateCompilationWithMscorlib461(source).VerifyDiagnostics(); } [Fact] @@ -697,10 +694,7 @@ unsafe async static Task M1(int* i) { } CreateCompilationWithMscorlib461(source, null, TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (6,38): error CS4005: Async methods cannot have pointer type parameters // unsafe async static Task M1(int* i) - Diagnostic(ErrorCode.ERR_UnsafeAsyncArgType, "i"), - // (6,30): 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. - // unsafe async static Task M1(ref int* i) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M1")); + Diagnostic(ErrorCode.ERR_UnsafeAsyncArgType, "i")); } [Fact] @@ -716,10 +710,7 @@ unsafe async static Task M1(delegate* i) { } CreateCompilationWithMscorlib461(source, null, TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (6,49): error CS4005: Async methods cannot have pointer type parameters // unsafe async static Task M1(delegate* i) { } - Diagnostic(ErrorCode.ERR_UnsafeAsyncArgType, "i").WithLocation(6, 49), - // (6,30): 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. - // unsafe async static Task M1(delegate* i) { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M1").WithLocation(6, 30) + Diagnostic(ErrorCode.ERR_UnsafeAsyncArgType, "i").WithLocation(6, 49) ); } @@ -766,10 +757,7 @@ unsafe async static Task M1(ref int* i) { } CreateCompilationWithMscorlib461(source, null, TestOptions.UnsafeReleaseDll).VerifyDiagnostics( // (6,42): error CS1988: Async methods cannot have ref, in or out parameters // unsafe async static Task M1(ref int* i) - Diagnostic(ErrorCode.ERR_BadAsyncArgType, "i"), - // (6,30): 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. - // unsafe async static Task M1(ref int* i) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M1")); + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "i")); } [Fact] @@ -1602,25 +1590,7 @@ static void Main() Func> f5 = async delegate () { return 1; }; } }"; - CreateCompilationWithMscorlib461(source).VerifyDiagnostics( - // (7,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. - // async static Task M() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(7, 23), - // (13,30): 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. - // Action f1 = async () => new Action(() => { })(); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(13, 30), - // (14,30): 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. - // Action f2 = async () => { }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(14, 30), - // (15,39): 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. - // Func> f3 = async () => { return 1; }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(15, 39), - // (16,27): 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. - // Action f4 = async delegate () { }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(16, 27), - // (17,36): 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. - // Func> f5 = async delegate () { return 1; }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(17, 36)); + CreateCompilationWithMscorlib461(source).VerifyDiagnostics(); } [Fact] @@ -1748,10 +1718,7 @@ static int Main() Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()"), // (27,13): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Goo(); - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()"), - // (31,32): 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. - // public async Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo")); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()")); } [Fact] @@ -1800,12 +1767,6 @@ static int Main() // (24,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // bar(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "bar()"), - // (19,31): 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 Task Meth() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth"), - // (29,33): 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 Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo"), // (36,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Goo(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()")); @@ -1854,16 +1815,7 @@ static int Main() Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "i.MethExt()").WithLocation(19, 9), // (20,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Goo(1); - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo(1)").WithLocation(20, 9), - // (16,26): 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 Task Meth(T t) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth").WithLocation(16, 26), - // (26,34): 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. - // return Task.Run(async () => { return t; }); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(26, 34), - // (10,34): 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. - // return Task.Run(async () => new C1()); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(10, 34)); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo(1)").WithLocation(20, 9)); } [Fact] @@ -1900,27 +1852,18 @@ static int Main() } }"; CreateCompilationWithMscorlib461(source, references: new MetadataReference[] { CSharpRef, SystemCoreRef }).VerifyDiagnostics( - // (20,32): 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 Task Meth1() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth1"), // (23,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Goo(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()"), // (23,9): warning CS0162: Unreachable code detected // Goo(); Diagnostic(ErrorCode.WRN_UnreachableCode, "Goo"), - // (27,33): 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 Task Meth2() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth2"), // (29,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Goo(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()"), // (31,9): warning CS0162: Unreachable code detected // return null; - Diagnostic(ErrorCode.WRN_UnreachableCode, "return"), - // (34,33): 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 Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo")); + Diagnostic(ErrorCode.WRN_UnreachableCode, "return")); } [Fact] @@ -1958,9 +1901,6 @@ static int Main() // (22,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Goo(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()"), - // (19,32): 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 Task Meth1() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth1"), // (22,9): warning CS0162: Unreachable code detected // Goo(); Diagnostic(ErrorCode.WRN_UnreachableCode, "Goo"), @@ -1969,10 +1909,7 @@ static int Main() Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Goo()"), // (28,9): warning CS0162: Unreachable code detected // Goo(); - Diagnostic(ErrorCode.WRN_UnreachableCode, "Goo"), - // (31,32): 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 Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo")); + Diagnostic(ErrorCode.WRN_UnreachableCode, "Goo")); } [Fact] @@ -2217,10 +2154,7 @@ static int Main() Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, @"Meth("""")"), // (36,13): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Meth((decimal?)2); - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Meth((decimal?)2)"), - // (24,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 Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo")); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Meth((decimal?)2)")); } [Fact] @@ -2394,13 +2328,8 @@ static int Main() Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, @"Meth("""", null)").WithLocation(19, 9), // (25,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // Meth(Task.Run(async () => 1), Meth(1)); - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Meth(Task.Run(async () => 1), Meth(1))").WithLocation(25, 9), - // (25,32): 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. - // Meth(Task.Run(async () => 1), Meth(1)); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(25, 32), - // (22,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. - // async public Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo").WithLocation(22, 23)); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "Meth(Task.Run(async () => 1), Meth(1))").WithLocation(25, 9) + ); } [WorkItem(611150, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/611150")] @@ -2538,10 +2467,8 @@ static int Main() Diagnostic(ErrorCode.ERR_IllegalStatement, "test.Prop"), // (48,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // test[1]; //error CS0201 - Diagnostic(ErrorCode.ERR_IllegalStatement, "test[1]"), - // (44,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. - // public async Task Goo() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo")); + Diagnostic(ErrorCode.ERR_IllegalStatement, "test[1]") + ); } [Fact] @@ -3044,9 +2971,6 @@ static int Main() } }"; CreateCompilationWithMscorlib461(source).VerifyDiagnostics( - // (14,34): 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. - // return Task.Run(async () => { return i; }); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(14, 34), // (21,13): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // test.Meth(); Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "test.Meth()").WithLocation(21, 13), @@ -3302,10 +3226,8 @@ unsafe async public static void F() Diagnostic(ErrorCode.ERR_BadFixedInitType, "tr"), // (8,31): error CS0210: You must provide an initializer in a fixed or using statement declaration // fixed (TypedReference tr) { } - Diagnostic(ErrorCode.ERR_FixedMustInit, "tr"), - // (6,37): 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. - // unsafe async public static void F() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F")); + Diagnostic(ErrorCode.ERR_FixedMustInit, "tr") + ); } [Fact] @@ -3475,10 +3397,7 @@ async void Meth() { } }"; - CreateCompilationWithMscorlib461(source).VerifyDiagnostics( - // (4,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 Meth() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Meth")); + CreateCompilationWithMscorlib461(source).VerifyDiagnostics(); } [Fact, WorkItem(547079, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547079")] @@ -3514,10 +3433,7 @@ async public static Task Main() { } }"; - CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics( - // (5,30): 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 public static Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(5, 30)); + CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7_1)).VerifyDiagnostics(); } [Fact, WorkItem(547081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547081")] @@ -3533,9 +3449,6 @@ async public static Task Main() }"; var comp = CreateCompilationWithMscorlib461(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp7)); comp.VerifyDiagnostics( - // (5,30): 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 public static Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(5, 30), // (5,25): error CS8107: Feature 'async main' is not available in C# 7.0. Please use language version 7.1 or greater. // async public static Task Main() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "Task").WithArguments("async main", "7.1").WithLocation(5, 25), @@ -3561,10 +3474,8 @@ public async void Goo(ref int x) CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (9,35): error CS1988: Async methods cannot have ref, in or out parameters // public async void Goo(ref int x) - Diagnostic(ErrorCode.ERR_BadAsyncArgType, "x"), - // (9,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. - // public async void Goo(ref int x) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo")); + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "x") + ); } [Fact] @@ -3726,10 +3637,8 @@ static void Main() CreateCompilationWithMscorlib461(source).VerifyDiagnostics( // (6,29): error CS4010: Cannot convert async anonymous method to delegate type 'Func'. An async anonymous method may return void, Task or Task, none of which are convertible to 'Func'. // Func x = async delegate { throw null; }; - Diagnostic(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, "delegate").WithArguments("anonymous method", "System.Func"), - // (6,29): 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. - // Func x = async delegate { throw null; }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(6, 29)); + Diagnostic(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, "delegate").WithArguments("anonymous method", "System.Func") + ); } [WorkItem(588706, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/588706")] @@ -3904,13 +3813,8 @@ Task YAsync() Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "YAsync()"), // (17,9): warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. // XAsync(); // warn - Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "XAsync()"), - // (14,30): 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. - // Action x2 = async () => XAsync(); // warn - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(14, 30), - // (15,30): 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. - // Action y2 = async () => YAsync(); // warn - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(15, 30)); + Diagnostic(ErrorCode.WRN_UnobservedAwaitableExpression, "XAsync()") + ); } [Fact()] @@ -3938,10 +3842,8 @@ static void F(Func f) { } static void F(Func> f) { } static void M() { -#pragma warning disable CS1998 F(async () => { }); F(async () => { return 3; }); -#pragma warning restore CS1998 } }"; var reference = CompileIL(ilSource); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAwaitTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAwaitTests.cs index eb4b6f91c74..f0edaad44be 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAwaitTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAwaitTests.cs @@ -2690,10 +2690,8 @@ public IVsTask ResolveReferenceAsync() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "IVsTask").WithArguments("IVsTask").WithLocation(4, 12), // (6,21): error CS1061: 'C' does not contain a definition for 'VsTasksService' and no extension method 'VsTasksService' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) // return this.VsTasksService.InvokeAsync(async delegate - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "VsTasksService").WithArguments("C", "VsTasksService").WithLocation(6, 21), - // (6,54): 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. - // return this.VsTasksService.InvokeAsync(async delegate - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(6, 54)); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "VsTasksService").WithArguments("C", "VsTasksService").WithLocation(6, 21) + ); } [Fact, WorkItem(627123, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627123")] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index acdc4f26fa4..0029ab5e4d2 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -19463,6 +19463,10 @@ .method assembly hidebysig int32[] ys ) cil managed { + .param [2] + .custom instance void [{{s_libPrefix}}]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) // Method begins at RVA 0x20b0 // Code size 1 (0x1) .maxstack 8 @@ -19520,6 +19524,10 @@ int32[] ys .custom instance void [{{s_libPrefix}}]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + .param [2] + .custom instance void [{{s_libPrefix}}]System.ParamArrayAttribute::.ctor() = ( + 01 00 00 00 + ) // Method begins at RVA 0x2067 // Code size 1 (0x1) .maxstack 8 diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/ImplicitObjectCreationTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/ImplicitObjectCreationTests.cs index 59424a53b7f..8195479a014 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/ImplicitObjectCreationTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/ImplicitObjectCreationTests.cs @@ -1454,8 +1454,7 @@ public static void Main() } "; - var comp = CreateCompilation(source, options: TestOptions.DebugExe).VerifyDiagnostics( - ); + var comp = CreateCompilation(source, options: TestOptions.DebugExe).VerifyDiagnostics(); CompileAndVerify(comp, expectedOutput: "Animal"); } @@ -2267,11 +2266,8 @@ static void M() comp.VerifyDiagnostics( // (8,9): error CS0411: The type arguments for method 'C.F(Task)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F(async () => new()); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("C.F(System.Threading.Tasks.Task)").WithLocation(8, 9), - // (8,20): 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. - // F(async () => new()); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(8, 20) - ); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("C.F(System.Threading.Tasks.Task)").WithLocation(8, 9) + ); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 254772b5d70..847427caa5e 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -6643,6 +6643,137 @@ .locals init (CustomHandler V_0) "); } + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79888")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PassAsRefWithoutKeyword_05_WithReorder(string refKind) + { + var code = $$""" + BadFunc( + message: $"abc", + value2: 0, + value1: 1); + + static void BadFunc( + {{refKind}} InterpolatedStringHandler message, + in int? value1, + int? value2) + { + System.Console.Write(value1); + System.Console.Write(value2); + } + + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public ref struct InterpolatedStringHandler + { + public InterpolatedStringHandler( + int literalLength, + int formattedCount) + { + } + + public void AppendLiteral(string value) + { + System.Console.Write(value); + } + } + """; + + var verifier = CompileAndVerify(code, targetFramework: TargetFramework.Net90, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "abc10" : null, verify: Verification.FailsPEVerify); + verifier.VerifyIL("", $$""" + { + // Code size 47 (0x2f) + .maxstack 3 + .locals init (int? V_0, + InterpolatedStringHandler V_1, + int? V_2) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.3 + IL_0003: ldc.i4.0 + IL_0004: call "InterpolatedStringHandler..ctor(int, int)" + IL_0009: ldloca.s V_1 + IL_000b: ldstr "abc" + IL_0010: call "void InterpolatedStringHandler.AppendLiteral(string)" + IL_0015: ldloca.s V_1 + IL_0017: ldloca.s V_0 + IL_0019: ldc.i4.0 + IL_001a: call "int?..ctor(int)" + IL_001f: ldc.i4.1 + IL_0020: newobj "int?..ctor(int)" + IL_0025: stloc.2 + IL_0026: ldloca.s V_2 + IL_0028: ldloc.0 + IL_0029: call "void Program.<
$>g__BadFunc|0_0({{refKind}} InterpolatedStringHandler, in int?, int?)" + IL_002e: ret + } + """); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79888")] + [InlineData("ref")] + [InlineData("ref readonly")] + [InlineData("in")] + public void PassAsRefWithoutKeyword_05_WithoutReorder(string refKind) + { + var code = $$""" + BadFunc( + message: $"abc", + value1: 1, + value2: 0); + + static void BadFunc( + {{refKind}} InterpolatedStringHandler message, + in int? value1, + int? value2) + { + System.Console.Write(value1); + System.Console.Write(value2); + } + + [System.Runtime.CompilerServices.InterpolatedStringHandler] + public ref struct InterpolatedStringHandler + { + public InterpolatedStringHandler( + int literalLength, + int formattedCount) + { + } + + public void AppendLiteral(string value) + { + System.Console.Write(value); + } + } + """; + + var verifier = CompileAndVerify(code, targetFramework: TargetFramework.Net90, expectedOutput: ExecutionConditionUtil.IsCoreClr ? "abc10" : null, verify: Verification.FailsPEVerify); + verifier.VerifyIL("", $$""" + { + // Code size 44 (0x2c) + .maxstack 3 + .locals init (InterpolatedStringHandler V_0, + int? V_1) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.3 + IL_0003: ldc.i4.0 + IL_0004: call "InterpolatedStringHandler..ctor(int, int)" + IL_0009: ldloca.s V_0 + IL_000b: ldstr "abc" + IL_0010: call "void InterpolatedStringHandler.AppendLiteral(string)" + IL_0015: ldloca.s V_0 + IL_0017: ldc.i4.1 + IL_0018: newobj "int?..ctor(int)" + IL_001d: stloc.1 + IL_001e: ldloca.s V_1 + IL_0020: ldc.i4.0 + IL_0021: newobj "int?..ctor(int)" + IL_0026: call "void Program.<
$>g__BadFunc|0_0({{refKind}} InterpolatedStringHandler, in int?, int?)" + IL_002b: ret + } + """); + } + [Theory] [CombinatorialData] public void RefOverloadResolution_Struct([CombinatorialValues("in", "ref")] string refKind, [CombinatorialValues(@"$""{1,2:f}Literal""", @"$""{1,2:f}"" + $""Literal""")] string expression) @@ -17581,7 +17712,7 @@ .locals init (CustomHandler V_0) IL_0053: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0058: ldloc.0 IL_0059: stelem ""CustomHandler"" - IL_005e: call ""void Program.<
$>g__M|0_0(CustomHandler[])"" + IL_005e: call ""void Program.<
$>g__M|0_0(params CustomHandler[])"" IL_0063: ret } "); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index ef06559513b..2ab9395a877 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -2866,11 +2866,8 @@ void M() Diagnostic(ErrorCode.ERR_NameNotInContext, "error").WithArguments("error").WithLocation(8, 13), // (6,93): error CS4010: Cannot convert async lambda expression to delegate type 'Task>'. An async lambda expression may return void, Task or Task, none of which are convertible to 'Task>'. // System.Func>> x = async x1 => x2 => - Diagnostic(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, "x2 =>").WithArguments("lambda expression", "System.Threading.Tasks.Task>").WithLocation(6, 93), - // (6,90): 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. - // System.Func>> x = async x1 => x2 => - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(6, 90) - ); + Diagnostic(ErrorCode.ERR_CantConvAsyncAnonFuncReturns, "x2 =>").WithArguments("lambda expression", "System.Threading.Tasks.Task>").WithLocation(6, 93) + ); } [Fact, WorkItem(22662, "https://github.com/dotnet/roslyn/issues/22662")] @@ -4061,10 +4058,8 @@ static void Main() Diagnostic(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, "SecurityCritical").WithArguments("SecurityCritical").WithLocation(8, 14), // (9,14): error CS4030: Security attribute 'SecuritySafeCriticalAttribute' cannot be applied to an Async method. // [SecuritySafeCriticalAttribute] // 2 - Diagnostic(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, "SecuritySafeCriticalAttribute").WithArguments("SecuritySafeCriticalAttribute").WithLocation(9, 14), - // (10,22): 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 () => { }; // 3 - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(10, 22)); + Diagnostic(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, "SecuritySafeCriticalAttribute").WithArguments("SecuritySafeCriticalAttribute").WithLocation(9, 14) + ); } [Fact] @@ -5432,10 +5427,8 @@ static void Main() comp.VerifyDiagnostics( // (6,35): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // Delegate d = async int () => 0; - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=>").WithLocation(6, 35), - // (6,35): 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. - // Delegate d = async int () => 0; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(6, 35)); + Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=>").WithLocation(6, 35) + ); } [Fact] @@ -8546,6 +8539,340 @@ public void ParamsArray_ThisModifier_02() Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(1, 19)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute() + { + var source = """ + using System; + using System.Linq; + using System.Reflection; + + var lam = (params int[] xs) => xs.Length; + Console.WriteLine(lam(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(Program) + .GetNestedType("<>c", BindingFlags.NonPublic) + .GetMethod("<
$>b__0_0", BindingFlags.Instance | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + """; + CompileAndVerify(source, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.ParamArrayAttribute + """) + .VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("Program.<>c.<
$>b__0_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Int32[] xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.True(parameter.IsParamsArray); + Assert.False(parameter.IsParamsCollection); + + Assert.DoesNotContain("ParamArrayAttribute", module.TypeNames); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute_ExtensionMethod() + { + var source = """ + using System; + using System.Linq; + using System.Reflection; + + object.M(); + + static class E + { + extension(object) + { + public static void M() + { + var lam = (params int[] xs) => xs.Length; + Console.WriteLine(lam(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(E) + .GetNestedType("<>c", BindingFlags.NonPublic) + .GetMethod("b__1_0", BindingFlags.Instance | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + } + } + } + """; + CompileAndVerify(source, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.ParamArrayAttribute + """) + .VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("E.<>c.b__1_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Int32[] xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.True(parameter.IsParamsArray); + Assert.False(parameter.IsParamsCollection); + + Assert.DoesNotContain("ParamArrayAttribute", module.TypeNames); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute_Missing() + { + var source = """ + var lam = (params int[] xs) => xs.Length; + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (1,12): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // var lam = (params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 12)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute_Missing_ExtensionMethod() + { + var source = """ + static class E + { + extension(object) + { + public static void M() + { + var lam = (params int[] xs) => xs.Length; + } + } + } + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (7,24): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // var lam = (params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(7, 24)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute(bool includeAttribute) + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + var lam = (params IList xs) => xs.Count; + Console.WriteLine(lam(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(Program) + .GetNestedType("<>c", BindingFlags.NonPublic) + .GetMethod("<
$>b__0_0", BindingFlags.Instance | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + """; + + var r = CreateCompilation(includeAttribute ? TestSources.ParamsCollectionAttribute : "").VerifyDiagnostics().ToMetadataReference(); + + CompileAndVerify(source, [r], + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.Runtime.CompilerServices.ParamCollectionAttribute + """) + .VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("Program.<>c.<
$>b__0_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Collections.Generic.IList xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.False(parameter.IsParamsArray); + Assert.True(parameter.IsParamsCollection); + + Assert.Equal(includeAttribute, !module.TypeNames.Contains("ParamCollectionAttribute")); + } + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_ExtensionMethod(bool includeAttribute) + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + object.M(); + + static class E + { + extension(object) + { + public static void M() + { + var lam = (params IList xs) => xs.Count; + Console.WriteLine(lam(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(E) + .GetNestedType("<>c", BindingFlags.NonPublic) + .GetMethod("b__1_0", BindingFlags.Instance | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + } + } + } + """; + + var r = CreateCompilation(includeAttribute ? TestSources.ParamsCollectionAttribute : "").VerifyDiagnostics().ToMetadataReference(); + + CompileAndVerify(source, [r], + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.Runtime.CompilerServices.ParamCollectionAttribute + """) + .VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("E.<>c.b__1_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Collections.Generic.IList xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.False(parameter.IsParamsArray); + Assert.True(parameter.IsParamsCollection); + + Assert.Equal(includeAttribute, !module.TypeNames.Contains("ParamCollectionAttribute")); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Missing() + { + var source = """ + using System.Collections.Generic; + class C + { + void M() + { + var lam = (params IList xs) => xs.Count; + } + } + """; + CreateCompilation(source, options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (6,20): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // var lam = (params IList xs) => xs.Count; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IList xs").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(6, 20)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Missing_NoSynthesizedDelegate() + { + var source = """ + using System; + using System.Collections.Generic; + class C + { + void M() + { + Func, int> lam = (params IList xs) => xs.Count; + lam([1, 2, 3]); + } + } + """; + CreateCompilation(source, options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (7,38): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // Func, int> lam = (params IList xs) => xs.Count; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IList xs").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(7, 38)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Missing_ExtensionMethod() + { + var source = """ + using System.Collections.Generic; + static class E + { + extension(object) + { + public static void M() + { + var lam = (params IList xs) => xs.Count; + } + } + } + """; + CreateCompilation([source, ExtensionMarkerAttributeDefinition], options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (8,24): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // var lam = (params IList xs) => xs.Count; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IList xs").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(8, 24)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Speculative() + { + // Compile without a need for the attribute. + var source1 = """ + class C + { + void M() + { + } + } + """; + var comp = CreateCompilation(source1); + var tree1 = comp.SyntaxTrees.Single(); + var model1 = comp.GetSemanticModel(tree1); + + // Speculatively bind a lambda that needs the attribute. + var source2 = """ + class C + { + void M() + { + var lam = (params System.Collections.Generic.IList xs) => xs.Count; + } + } + """; + var tree2 = CSharpSyntaxTree.ParseText(source2); + var method1 = tree1.GetRoot().DescendantNodes().OfType().Single(); + var method2 = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.True(model1.TryGetSpeculativeSemanticModelForMethodBody(method1.Body.SpanStart, method2, out var model2)); + var lambda = tree2.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model2.GetSymbolInfo(lambda).Symbol; + Assert.NotNull(symbol); + Assert.True(symbol.GetParameters().Single().IsParamsCollection); + + // The original compilation does not synthesize the attribute when emitted. + Assert.Equal(TypeKind.Error, comp.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_ParamCollectionAttribute).TypeKind); + CompileAndVerify(comp, + symbolValidator: static (module) => + { + Assert.DoesNotContain("ParamCollectionAttribute", module.TypeNames); + }) + .VerifyDiagnostics(); + } + [Fact] public void ImplicitlyTypedLambdaWithModifier_CSharp13() { @@ -8870,5 +9197,87 @@ static void validate(ModuleSymbol m) m.GlobalNamespace.GetMember("Test1.<>c__DisplayClass0_0.x").GetAttributes().Select(a => a.ToString())); } } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/79130")] + public void ConstrainedTypeParameter() + { + CompileAndVerify(@" +using System; +using System.Linq.Expressions; + +class Program +{ + static void Main() + { + var expressionCreatedDirectly = ExpressionBuilder.OrderBy(x => x.IntProperty); + var expressionViaGenericMethodConstrainedToClass = CreateExpressionConstrainedToClass(); + var expressionViaGenericMethodConstrainedToInterface = CreateExpressionConstrainedToInterface(); + var expressionViaGenericMethodConstrainedToInterfaceAndClass = CreateExpressionConstrainedToInterfaceAndClass(); + + AssertIsMemberExpressionWithParameterExpression(expressionCreatedDirectly); + AssertIsMemberExpressionWithParameterExpression(expressionViaGenericMethodConstrainedToClass); + AssertIsMemberExpressionWithParameterExpression(expressionViaGenericMethodConstrainedToInterface); + AssertIsMemberExpressionWithParameterExpression(expressionViaGenericMethodConstrainedToInterfaceAndClass); + } + + static Expression> CreateExpressionConstrainedToClass() + where T : ClassWithIntProperty + { + return ExpressionBuilder.OrderBy(x => x.IntProperty); + } + + static Expression> CreateExpressionConstrainedToInterface() + where T : IWithIntProperty + { + return ExpressionBuilder.OrderBy(x => x.IntProperty); + } + + static Expression> CreateExpressionConstrainedToInterfaceAndClass() + where T : class, IWithIntProperty + { + return ExpressionBuilder.OrderBy(x => x.IntProperty); + } + + static void AssertIsMemberExpressionWithParameterExpression(Expression> expression) + { + var memberExpression = (MemberExpression)expression.Body; + var nestedExpression = memberExpression.Expression; + Console.WriteLine(nestedExpression.NodeType); + Console.WriteLine(memberExpression); + } +} + +static class ExpressionBuilder +{ + public static Expression> OrderBy( + Expression> keySelector) + { + return keySelector; + } +} + +public interface IWithIntProperty +{ + int IntProperty { get; } +} + +class ClassWithIntProperty : IWithIntProperty +{ + public int IntProperty { get; set; } +} +", + options: TestOptions.DebugExe, + expectedOutput: @" +Parameter +x.IntProperty +Parameter +x.IntProperty +Convert +Convert(x" + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", IWithIntProperty" : "") + @").IntProperty +Parameter +x.IntProperty +"); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 7706b5b47a3..be665d4b138 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -52,16 +52,10 @@ async ref System.Threading.Tasks.Task local() { } // (4,11): error CS1073: Unexpected token 'ref' // async ref System.Threading.Tasks.Task M() { } Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 11), - // (4,43): 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 ref System.Threading.Tasks.Task M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 43), // (10,15): error CS1073: Unexpected token 'ref' // async ref System.Threading.Tasks.Task local() { } - Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(10, 15), - // (10,47): 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 ref System.Threading.Tasks.Task local() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "local").WithLocation(10, 47) - ); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(10, 15) + ); } [ConditionalFact(typeof(DesktopOnly))] @@ -2067,10 +2061,8 @@ async void local1() // 3 Diagnostic(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, "SecurityCritical").WithArguments("SecurityCritical").WithLocation(10, 10), // (11,10): error CS4030: Security attribute 'SecuritySafeCriticalAttribute' cannot be applied to an Async method. // [SecuritySafeCriticalAttribute] // 2 - Diagnostic(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, "SecuritySafeCriticalAttribute").WithArguments("SecuritySafeCriticalAttribute").WithLocation(11, 10), - // (12,20): 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 local1() // 3 - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "local1").WithLocation(12, 20)); + Diagnostic(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, "SecuritySafeCriticalAttribute").WithArguments("SecuritySafeCriticalAttribute").WithLocation(11, 10) + ); } [Fact] @@ -2601,6 +2593,319 @@ public void ParamsArray_Symbol_MultipleParamsArrays() Assert.True(methods[1].Parameters[2].IsParams); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute() + { + var source = """ + using System; + using System.Linq; + using System.Reflection; + + int fun(params int[] xs) => xs.Length; + Console.WriteLine(fun(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(Program) + .GetMethod("<
$>g__fun|0_0", BindingFlags.Static | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + """; + CompileAndVerify(source, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.ParamArrayAttribute + """) + .VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("Program.<
$>g__fun|0_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Int32[] xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.True(parameter.IsParamsArray); + Assert.False(parameter.IsParamsCollection); + + Assert.DoesNotContain("ParamArrayAttribute", module.TypeNames); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute_ExtensionMethod() + { + var source = """ + using System; + using System.Linq; + using System.Reflection; + + object.M(); + + static class E + { + extension(object) + { + public static void M() + { + int fun(params int[] xs) => xs.Length; + Console.WriteLine(fun(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(E) + .GetMethod("g__fun|1_0", BindingFlags.Static | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + } + } + } + """; + CompileAndVerify(source, + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.ParamArrayAttribute + """) + .VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("E.g__fun|1_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Int32[] xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.True(parameter.IsParamsArray); + Assert.False(parameter.IsParamsCollection); + + Assert.DoesNotContain("ParamArrayAttribute", module.TypeNames); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute_Missing() + { + var source = """ + int fun(params int[] xs) => xs.Length; + fun(4, 5, 6); + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (1,9): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // int fun(params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params int[] xs").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(1, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsArray_Attribute_Missing_ExtensionMethod() + { + var source = """ + static class E + { + extension(object) + { + public static void M() + { + int fun(params int[] xs) => xs.Length; + fun(4, 5, 6); + } + } + } + """; + var comp = CreateCompilation(source); + comp.MakeTypeMissing(WellKnownType.System_ParamArrayAttribute); + comp.VerifyDiagnostics( + // (7,21): error CS0656: Missing compiler required member 'System.ParamArrayAttribute..ctor' + // int fun(params int[] xs) => xs.Length; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "params int[] xs").WithArguments("System.ParamArrayAttribute", ".ctor").WithLocation(7, 21)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute(bool includeAttribute) + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + int fun(params IList xs) => xs.Count; + Console.WriteLine(fun(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(Program) + .GetMethod("<
$>g__fun|0_0", BindingFlags.Static | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + """; + + var r = CreateCompilation(includeAttribute ? TestSources.ParamsCollectionAttribute : "").VerifyDiagnostics().ToMetadataReference(); + + CompileAndVerify(source, [r], + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.Runtime.CompilerServices.ParamCollectionAttribute + """) + .VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("Program.<
$>g__fun|0_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Collections.Generic.IList xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.False(parameter.IsParamsArray); + Assert.True(parameter.IsParamsCollection); + + Assert.Equal(includeAttribute, !module.TypeNames.Contains("ParamCollectionAttribute")); + } + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_ExtensionMethod(bool includeAttribute) + { + var source = """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + object.M(); + + static class E + { + extension(object) + { + public static void M() + { + int fun(params IList xs) => xs.Count; + Console.WriteLine(fun(4, 5, 6)); + Console.WriteLine(string.Join("\n", typeof(E) + .GetMethod("g__fun|1_0", BindingFlags.Static | BindingFlags.NonPublic) + .GetParameters() + .Single() + .CustomAttributes + .Select(a => a.AttributeType))); + } + } + } + """; + + var r = CreateCompilation(includeAttribute ? TestSources.ParamsCollectionAttribute : "").VerifyDiagnostics().ToMetadataReference(); + + CompileAndVerify(source, [r], + options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + expectedOutput: """ + 3 + System.Runtime.CompilerServices.ParamCollectionAttribute + """) + .VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var lambda = module.GlobalNamespace.GetMember("E.g__fun|1_0"); + var parameter = lambda.GetParameters().Single(); + AssertEx.Equal("params System.Collections.Generic.IList xs", parameter.ToTestDisplayString()); + Assert.True(parameter.IsParams); + Assert.False(parameter.IsParamsArray); + Assert.True(parameter.IsParamsCollection); + + Assert.Equal(includeAttribute, !module.TypeNames.Contains("ParamCollectionAttribute")); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Missing() + { + var source = """ + using System.Collections.Generic; + class C + { + void M() + { + int func(params IList xs) => xs.Count; + func(4, 5, 6); + } + } + """; + CreateCompilation(source, options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (6,18): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // int func(params IList xs) => xs.Count; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IList xs").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(6, 18)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Missing_ExtensionMethod() + { + var source = """ + using System.Collections.Generic; + static class E + { + extension(object) + { + public static void M() + { + int func(params IList xs) => xs.Count; + func(4, 5, 6); + } + } + } + """; + CreateCompilation([source, ExtensionMarkerAttributeDefinition], options: TestOptions.ReleaseModule).VerifyDiagnostics( + // (8,22): error CS0518: Predefined type 'System.Runtime.CompilerServices.ParamCollectionAttribute' is not defined or imported + // int func(params IList xs) => xs.Count; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "params IList xs").WithArguments("System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(8, 22)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79752")] + public void ParamsCollection_Attribute_Speculative() + { + // Compile without a need for the attribute. + var source1 = """ + class C + { + void M() + { + } + } + """; + var comp = CreateCompilation(source1); + var tree1 = comp.SyntaxTrees.Single(); + var model1 = comp.GetSemanticModel(tree1); + + // Speculatively bind a lambda that needs the attribute. + var source2 = """ + class C + { + void M() + { + int func(params System.Collections.Generic.IList xs) => xs.Count; + } + } + """; + var tree2 = CSharpSyntaxTree.ParseText(source2); + var method1 = tree1.GetRoot().DescendantNodes().OfType().Single(); + var method2 = tree2.GetRoot().DescendantNodes().OfType().Single(); + Assert.True(model1.TryGetSpeculativeSemanticModelForMethodBody(method1.Body.SpanStart, method2, out var model2)); + var func = tree2.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model2.GetDeclaredSymbol(func); + Assert.NotNull(symbol); + Assert.True(symbol.Parameters.Single().IsParamsCollection); + + // The original compilation does not synthesize the attribute when emitted. + Assert.Equal(TypeKind.Error, comp.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_ParamCollectionAttribute).TypeKind); + CompileAndVerify(comp, + symbolValidator: static (module) => + { + Assert.DoesNotContain("ParamCollectionAttribute", module.TypeNames); + }) + .VerifyDiagnostics(); + } + [Fact] public void BadRefWithDefault() { @@ -5017,10 +5322,8 @@ async Task L(Func t, object p) Diagnostic(ErrorCode.ERR_BadDynamicMethodArgLambda, "async d => await d").WithLocation(8, 37), // (8,35): error CS8322: Cannot pass argument with dynamic type to generic local function 'L' with inferred type arguments. // => await L(async m => L(async d => await d, m), p); - Diagnostic(ErrorCode.ERR_DynamicLocalFunctionTypeParameter, "L(async d => await d, m)").WithArguments("L").WithLocation(8, 35), - // (8,32): 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. - // => await L(async m => L(async d => await d, m), p); - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(8, 32)); + Diagnostic(ErrorCode.ERR_DynamicLocalFunctionTypeParameter, "L(async d => await d, m)").WithArguments("L").WithLocation(8, 35) + ); } [Fact] @@ -7113,7 +7416,7 @@ static void F2() public void AwaitWithinAsyncOuterScope_01() { var source = -@"#pragma warning disable 1998 +@" #pragma warning disable 8321 using System.Threading.Tasks; class Program @@ -7163,7 +7466,7 @@ async void F4() public void AwaitWithinAsyncOuterScope_02() { var source = -@"#pragma warning disable 1998 +@" #pragma warning disable 8321 class Program { @@ -10193,7 +10496,6 @@ public void DefiniteAssignment_01() using System.Threading.Tasks; #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. -#pragma warning disable 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. public class C { @@ -10217,9 +10519,9 @@ async Task M1() } "; CreateCompilation(text).VerifyDiagnostics( - // (14,27): error CS0165: Use of unassigned local variable 'a' + // (13,27): error CS0165: Use of unassigned local variable 'a' // Console.WriteLine(a); - Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(14, 27) + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(13, 27) ); } @@ -10302,8 +10604,6 @@ public void DefiniteAssignment_04() using System; using System.Threading.Tasks; -#pragma warning disable 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. - public class C { public async Task M() @@ -10326,9 +10626,9 @@ async Task M1() } "; CreateCompilation(text).VerifyDiagnostics( - // (13,27): error CS0165: Use of unassigned local variable 'a' + // (11,27): error CS0165: Use of unassigned local variable 'a' // Console.WriteLine(a); - Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(13, 27) + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(11, 27) ); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index f0142285602..6fde5948be1 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -8667,10 +8667,7 @@ static async Task G(Func> f) } }"; var comp = CreateCompilationWithMscorlib461(source, parseOptions: TestOptions.Regular7); - comp.VerifyEmitDiagnostics( - // (13,32): 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 Task G(Func> f) - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "G").WithLocation(13, 32)); + comp.VerifyEmitDiagnostics(); } [Fact, WorkItem(26739, "https://github.com/dotnet/roslyn/issues/26618")] @@ -20420,7 +20417,7 @@ class C public void IdentityConversion_Return_02() { var source = -@"#pragma warning disable 1998 +@" using System.Threading.Tasks; interface I { } interface IIn { } @@ -21176,7 +21173,11 @@ public void M() c.VerifyEmitDiagnostics( // (10,9): error CS7036: There is no argument given that corresponds to the required parameter 'a' of 'init(int)' // init(); - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "init").WithArguments("a", "init(int)").WithLocation(10, 9)); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "init").WithArguments("a", "init(int)").WithLocation(10, 9), + // (12,9): warning CS8602: Dereference of a possibly null reference. + // field2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field2").WithLocation(12, 9) + ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56256")] @@ -78338,8 +78339,6 @@ Task F3(Class1 class1, Class3 class3) => "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // public async Task F1(Class3 a, int? b) => throw null!; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F1").WithLocation(21, 27), // (26,20): error CS1503: Argument 1: cannot convert from 'ConsoleApp1.Class3' to 'ConsoleApp1.Class3' // F1(class3, false ? Class1.Field1 : null); Diagnostic(ErrorCode.ERR_BadArgType, "class3").WithArguments("1", "ConsoleApp1.Class3", "ConsoleApp1.Class3").WithLocation(26, 20), @@ -83196,7 +83195,7 @@ class C public void AsyncTaskMethodReturningNull() { var source = -@"#pragma warning disable 1998 +@" using System.Threading.Tasks; class C { @@ -93785,14 +93784,14 @@ public void TypeInference_LowerBounds_NestedNullability_Pointers() static void G(object?* x, object* y) // 1 { _ = z/*T:object**/; - F(x, x)/*T:object?**/; // 2 - F(x, y)/*T:object!**/; // 3 - F(x, z)/*T:object?**/; // 4 - F(y, x)/*T:object!**/; // 5 - F(y, y)/*T:object!**/; // 6 - F(y, z)/*T:object!**/; // 7 - F(z, x)/*T:object?**/; // 8 - F(z, y)/*T:object!**/; // 9 + F(x, x)/*T:object**/; // 2 + F(x, y)/*T:object**/; // 3 + F(x, z)/*T:object**/; // 4 + F(y, x)/*T:object**/; // 5 + F(y, y)/*T:object**/; // 6 + F(y, z)/*T:object**/; // 7 + F(z, x)/*T:object**/; // 8 + F(z, y)/*T:object**/; // 9 F(z, z)/*T:object**/; // 10 } @@ -131195,9 +131194,6 @@ static void Extension(this int? self) { } // (12,9): warning CS8629: Nullable value type may be null. // x.GetType(); // warning1 Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "x").WithLocation(12, 9), - // (15,9): warning CS8629: Nullable value type may be null. - // y.MemberwiseClone(); // warning2 - Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "y").WithLocation(15, 9), // (15,11): error CS1540: Cannot access protected member 'object.MemberwiseClone()' via a qualifier of type 'int?'; the qualifier must be of type 'Program' (or derived from it) // y.MemberwiseClone(); // warning2 Diagnostic(ErrorCode.ERR_BadProtectedAccess, "MemberwiseClone").WithArguments("object.MemberwiseClone()", "int?", "Program").WithLocation(15, 11), @@ -147317,6 +147313,40 @@ class Program ); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80255")] + public void ReinferMethod_Span_01() + { + var source = """ + #nullable enable + using System; + class C + { + bool M(ReadOnlySpan a, Span b) + { + return b == a; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80255")] + public void ReinferMethod_Span_02() + { + var source = """ + #nullable enable + using System; + class C + { + bool M(ReadOnlySpan a, Span b) + { + return a == b; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics(); + } + [Fact] [WorkItem(38726, "https://github.com/dotnet/roslyn/issues/38726")] public void CollectionInitializerBoxingConversion() @@ -161412,9 +161442,6 @@ public class C // (5,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // async (string s) => { try {} catch (System.Exception e) {} }; Diagnostic(ErrorCode.ERR_IllegalStatement, "async (string s) => { try {} catch (System.Exception e) {} }").WithLocation(5, 1), - // (5,18): 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 (string s) => { try {} catch (System.Exception e) {} }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(5, 18), // (5,54): warning CS0168: The variable 'e' is declared but never used // async (string s) => { try {} catch (System.Exception e) {} }; Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(5, 54) @@ -161439,9 +161466,6 @@ public void Repro66960_2() // (5,1): warning CS0162: Unreachable code detected // async (string s) => { try {} catch (System.Exception e) {} }; Diagnostic(ErrorCode.WRN_UnreachableCode, "async").WithLocation(5, 1), - // (5,18): 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 (string s) => { try {} catch (System.Exception e) {} }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(5, 18), // (5,54): warning CS0168: The variable 'e' is declared but never used // async (string s) => { try {} catch (System.Exception e) {} }; Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(5, 54) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs index 2b6444eb74a..9b3097ff2ba 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionTests.cs @@ -7704,7 +7704,6 @@ class C { static void Main() { -#pragma warning disable 1998 Goo(async () => { return 0; ; }); Goo(async () => { return 0; }); Goo(async () => 0); @@ -7739,7 +7738,6 @@ class C { static void Main() { -#pragma warning disable 1998 Goo(() => async () => { return 0; ; }); Goo(() => async () => { return 0; }); Goo(() => async () => 0); @@ -7771,7 +7769,6 @@ class C { static void Main() { -#pragma warning disable 1998 Goo(async () => { return () => 0; }); Goo(async () => { return () => (short)0; }); } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index 1e01cc6afd4..1a66643b57f 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -12737,7 +12737,7 @@ .locals init (CustomHandler V_0) IL_0053: call ""void CustomHandler.AppendFormatted(object, int, string)"" IL_0058: ldloc.0 IL_0059: stelem ""CustomHandler"" - IL_005e: call ""void Program.<
$>g__M|0_0(CustomHandler[])"" + IL_005e: call ""void Program.<
$>g__M|0_0(params CustomHandler[])"" IL_0063: ret } "); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 18a01a86357..f6545643f1c 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -31991,5 +31991,135 @@ static void Test() // M(GetValue().F); Diagnostic(ErrorCode.ERR_BadArgRef, "GetValue().F").WithArguments("1", "ref").WithLocation(11, 11)); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80244")] + public void Repro_80244_NetCoreApp() + { + var comp = CreateCompilation(""" + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + ref struct SpanReader + { + long _spanEndStreamOffset; + ReadOnlySpan _buffer; + public SpanReader(ReadOnlySpan buffer, long spanStartStreamOffset) + { + _buffer = buffer; + _spanEndStreamOffset = spanStartStreamOffset + buffer.Length; + } + + public ref readonly T ReadRef() where T : struct + { + if (_buffer.Length >= Unsafe.SizeOf()) + { + ref readonly T ret = ref MemoryMarshal.Cast(_buffer)[0]; + _buffer = _buffer.Slice(Unsafe.SizeOf()); + return ref ret; + } + else + { + throw new Exception(); + } + } + } + """, + targetFramework: TargetFramework.NetCoreApp, + parseOptions: TestOptions.Regular14); + comp.VerifyEmitDiagnostics(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80244")] + [InlineData(LanguageVersion.CSharp8), InlineData(LanguageVersion.CSharp14)] + public void Repro_80244_NetStandard(LanguageVersion consumerLanguageVersion) + { + var spanCompilation = CreateCompilation(TestSources.Span, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular8); + var spanReference = spanCompilation.EmitToImageReference(); + var source0 = """ + namespace System.Runtime.CompilerServices + { + public static class Unsafe + { + public static int SizeOf() => throw null!; + } + } + + namespace System.Runtime.InteropServices + { + public static class MemoryMarshal + { + public static ReadOnlySpan Cast(ReadOnlySpan span) + where TFrom : struct + => throw null!; + } + } + """; + var source1 = """ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + ref struct SpanReader + { + long _spanEndStreamOffset; + ReadOnlySpan _buffer; + public SpanReader(ReadOnlySpan buffer, long spanStartStreamOffset) + { + _buffer = buffer; + _spanEndStreamOffset = spanStartStreamOffset + buffer.Length; + } + + public ref readonly T ReadRef() where T : struct + { + if (_buffer.Length >= Unsafe.SizeOf()) + { + ref readonly T ret = ref MemoryMarshal.Cast(_buffer)[0]; + _buffer = _buffer.Slice(Unsafe.SizeOf()); + return ref ret; + } + else + { + throw new Exception(); + } + } + } + """; + var comp = CreateCompilation([source0, source1], + references: [spanReference], + parseOptions: TestOptions.Regular.WithLanguageVersion(consumerLanguageVersion), + targetFramework: TargetFramework.NetStandard20); + comp.VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80244")] + public void Repro_80244_Simple() + { + var source0 = """ + public ref struct RS + { + public ref byte this[int i] => throw null!; + } + """; + + var reference = CreateCompilation(source0, parseOptions: TestOptions.Regular8).EmitToImageReference(); + var source1 = """ + class Program + { + static ref byte M1(RS rs) + { + ref byte ret = ref rs[1]; + return ref ret; + } + + static ref byte M2(RS rs) + { + return ref rs[1]; + } + } + """; + var comp = CreateCompilation(source1, references: [reference], parseOptions: TestOptions.Regular8); + comp.VerifyEmitDiagnostics(); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 48e81057165..d7fb8d2f728 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -3807,10 +3807,7 @@ static async void Goo() } "; - 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)); + CreateCompilationWithMscorlib46(text).VerifyEmitDiagnostics(); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs index 39ce17e008c..4d399fc429e 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs @@ -1104,7 +1104,7 @@ public void LocalDeclarationStatement_17() CompileAndVerify(comp, expectedOutput: "Hi!"); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80313")] public void LocalDeclarationStatement_18() { var text = @" @@ -1133,6 +1133,50 @@ public void LocalDeclarationStatement_18() comp = CreateCompilation(text, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics(); + + comp = CreateRuntimeAsyncCompilation(text); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = "[
$]: Return value missing on the stack. { Offset = 0x2f }" + }, sourceSymbolValidator: validator); + verifier.VerifyIL("", """ + { + // Code size 48 (0x30) + .maxstack 1 + .locals init (int V_0, //c + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2) + IL_0000: ldc.i4.s -100 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: ldind.i4 + IL_0006: call "void System.Console.Write(int)" + IL_000b: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0010: stloc.2 + IL_0011: ldloca.s V_2 + IL_0013: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0018: stloc.1 + IL_0019: ldloca.s V_1 + IL_001b: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0020: brtrue.s IL_0028 + IL_0022: ldloc.1 + IL_0023: call "void System.Runtime.CompilerServices.AsyncHelpers.UnsafeAwaitAwaiter(System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter)" + IL_0028: ldloca.s V_1 + IL_002a: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_002f: ret + } + """); + + static void validator(ModuleSymbol module) + { + var program = module.GlobalNamespace.GetTypeMember("Program"); + Assert.NotNull(program); + + var main = program.GetMethod("
$"); + Assert.NotNull(main); + Assert.Equal(MethodImplAttributes.Async, main.ImplementationAttributes & MethodImplAttributes.Async); + } } [Fact] @@ -7825,7 +7869,7 @@ public void Return_03() } } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80313")] public void Return_04() { var text = @" @@ -7893,6 +7937,51 @@ public void Return_04() ", options: PdbValidationOptions.SkipConversionValidation); } + + comp = CreateRuntimeAsyncCompilation(text); + // https://github.com/dotnet/roslyn/issues/79791: Verify runtime async output + var verifier = CompileAndVerify(comp, expectedOutput: null, verify: Verification.Fails with + { + ILVerifyMessage = "[
$]: Unexpected type on the stack. { Offset = 0x43, Found = Int32, Expected = ref '[System.Runtime]System.Threading.Tasks.Task`1' }" + }, sourceSymbolValidator: validator); + + verifier.VerifyIL("", """ + { + // Code size 68 (0x44) + .maxstack 3 + IL_0000: ldstr "hello " + IL_0005: call "void System.Console.Write(string)" + IL_000a: call "System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get" + IL_000f: ldsfld "System.Func Program.<>c.<>9__0_0" + IL_0014: dup + IL_0015: brtrue.s IL_002e + IL_0017: pop + IL_0018: ldsfld "Program.<>c Program.<>c.<>9" + IL_001d: ldftn "int Program.<>c.<
$>b__0_0()" + IL_0023: newobj "System.Func..ctor(object, System.IntPtr)" + IL_0028: dup + IL_0029: stsfld "System.Func Program.<>c.<>9__0_0" + IL_002e: callvirt "System.Threading.Tasks.Task System.Threading.Tasks.TaskFactory.StartNew(System.Func)" + IL_0033: call "int System.Runtime.CompilerServices.AsyncHelpers.Await(System.Threading.Tasks.Task)" + IL_0038: pop + IL_0039: ldarg.0 + IL_003a: ldc.i4.0 + IL_003b: ldelem.ref + IL_003c: call "void System.Console.Write(string)" + IL_0041: ldc.i4.s 11 + IL_0043: ret + } + """); + + static void validator(ModuleSymbol module) + { + var program = module.GlobalNamespace.GetTypeMember("Program"); + Assert.NotNull(program); + + var main = program.GetMethod("
$"); + Assert.NotNull(main); + Assert.Equal(MethodImplAttributes.Async, main.ImplementationAttributes & MethodImplAttributes.Async); + } } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TryCatchTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TryCatchTests.cs index 8a20dbabd67..09e6dac3cbd 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TryCatchTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Semantic/Semantics/TryCatchTests.cs @@ -154,11 +154,7 @@ private async Task M(object o) } """; - CompileAndVerify(source, options: TestOptions.DebugDll).VerifyDiagnostics( - // (13,37): 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. - // Func f = async () => - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(13, 37) - ); + CompileAndVerify(source, options: TestOptions.DebugDll).VerifyDiagnostics(); } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index 1e610d2734b..60f800d5fab 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -6105,5 +6105,41 @@ public static void ConvertAll(IEnumerable values) ReceiverType.Name: nameof(Enumerable), }); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79930")] + public void NotInvoicableInvocationTarget() + { + var source = +@" +public class OuterClass +{ + void M() + { + var s = OuterClass.InnerClass(); + } + + public class InnerClass + { + } +} +"; + var comp = CreateCompilation(source); + + comp.VerifyDiagnostics( + // (6,28): error CS1955: Non-invocable member 'OuterClass.InnerClass' cannot be used like a method. + // var s = OuterClass.InnerClass(); + Diagnostic(ErrorCode.ERR_NonInvocableMemberCalled, "InnerClass").WithArguments("OuterClass.InnerClass").WithLocation(6, 28) + ); + + var tree = comp.SyntaxTrees[0]; + var semanticModel = comp.GetSemanticModel(tree); + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + var expressionNode = invocationNode.Expression; + + var info = semanticModel.GetSymbolInfo(expressionNode); + Assert.Null(info.Symbol); + Assert.Equal(CandidateReason.NotInvocable, info.CandidateReason); + Assert.Same(comp.GetTypeByMetadataName("OuterClass+InnerClass").GetPublicSymbol(), info.CandidateSymbols.Single()); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs index ffd9ddaf95b..7197502def3 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs @@ -268,14 +268,18 @@ class K "; var semanticInfo = GetSemanticInfoForTest(sourceCode); - Assert.Null(semanticInfo.Type); - Assert.Null(semanticInfo.ConvertedType); + Assert.Equal("System.Int32", semanticInfo.Type.ToTestDisplayString()); + Assert.Equal(TypeKind.Struct, semanticInfo.Type.TypeKind); + Assert.Equal("System.Int32", semanticInfo.ConvertedType.ToTestDisplayString()); + Assert.Equal(TypeKind.Struct, semanticInfo.ConvertedType.TypeKind); Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind); Assert.Null(semanticInfo.Symbol); - Assert.Equal(CandidateReason.None, semanticInfo.CandidateReason); - // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, see if we can restore a behavior closer to previous (ie. returning the field as candidate symbol) - Assert.Empty(semanticInfo.CandidateSymbols); + Assert.Equal(CandidateReason.NotInvocable, semanticInfo.CandidateReason); + Assert.Equal(1, semanticInfo.CandidateSymbols.Length); + var sortedCandidates = semanticInfo.CandidateSymbols.OrderBy(s => s.ToTestDisplayString(), StringComparer.Ordinal).ToArray(); + Assert.Equal("System.Int32 K.f", sortedCandidates[0].ToTestDisplayString()); + Assert.Equal(SymbolKind.Field, sortedCandidates[0].Kind); Assert.Equal(0, semanticInfo.MethodGroup.Length); Assert.False(semanticInfo.IsCompileTimeConstant); diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs index 2f1f955c3ec..c7de2d9f505 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs @@ -417,9 +417,6 @@ async void AM() { } // NOTE: As in dev11, we don't consider myTask::System.Threading.Tasks.Task to be // ambiguous with global::System.Threading.Tasks.Task (prefer global). comp.VerifyDiagnostics( - // (7,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 AM() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "AM"), // (3,1): info CS8019: Unnecessary using directive. // using System.Threading; Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Threading;"), diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs index e3e910f796f..023fa631f01 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ExtendedPartialMethodsTests.cs @@ -1256,10 +1256,7 @@ async partial void M1() { } } "; var comp = CreateCompilation(text); - comp.VerifyDiagnostics( - // (5,24): 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 partial void M1() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M1").WithLocation(5, 24)); + comp.VerifyDiagnostics(); var method = (MethodSymbol)comp.GetMembers("C.M1")[0]; Assert.True(method.IsPartialDefinition()); @@ -1280,10 +1277,7 @@ private async partial Task M1() { } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.RegularWithExtendedPartialMethods); - comp.VerifyDiagnostics( - // (7,32): 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. - // private async partial Task M1() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M1").WithLocation(7, 32)); + comp.VerifyDiagnostics(); var method = (MethodSymbol)comp.GetMembers("C.M1")[0]; Assert.True(method.IsPartialDefinition()); @@ -1312,10 +1306,7 @@ public static async Task Main() } "; var verifier = CompileAndVerify(text, parseOptions: TestOptions.RegularWithExtendedPartialMethods, expectedOutput: "1"); - verifier.VerifyDiagnostics( - // (7,31): 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. - // private static async Task CompletedTask() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "CompletedTask").WithLocation(7, 31)); + verifier.VerifyDiagnostics(); var method = (MethodSymbol)verifier.Compilation.GetMembers("C.M1")[0]; Assert.True(method.IsPartialDefinition()); @@ -1753,8 +1744,7 @@ internal partial void M(string s1) { } } "; var comp = CreateCompilation(text, parseOptions: TestOptions.RegularWithExtendedPartialMethods, targetFramework: TargetFramework.NetCoreApp); - comp.VerifyDiagnostics( - ); + comp.VerifyDiagnostics(); } [Fact] @@ -2755,10 +2745,7 @@ public static partial async Task Main() parseOptions: TestOptions.RegularWithExtendedPartialMethods, options: TestOptions.DebugExe, expectedOutput: "1"); - verifier.VerifyDiagnostics( - // (8,38): 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. - // public static partial async Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(8, 38)); + verifier.VerifyDiagnostics(); } [Fact] @@ -2804,10 +2791,7 @@ public static partial async Task Main() parseOptions: TestOptions.RegularWithExtendedPartialMethods, options: TestOptions.DebugExe, expectedOutput: "1"); - verifier.VerifyDiagnostics( - // (8,43): 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. - // public static partial async Task Main() - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Main").WithLocation(8, 43)); + verifier.VerifyDiagnostics(); } [ConditionalFact(typeof(CoreClrOnly))] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs index 643950825aa..f7147016931 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs @@ -202,11 +202,8 @@ public sealed override bool IsRefLikeType } } - internal override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); - - internal override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); + internal sealed override string ExtensionGroupingName => null; + internal sealed override string ExtensionMarkerName => null; public sealed override bool IsReadOnly { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs index d15621ed9b8..d75c3b16181 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializers/SignatureTests.cs @@ -196,10 +196,8 @@ namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : S compilation.VerifyEmitDiagnostics( // (6,6): error CS8815: Module initializer method 'M' must be static, and non-virtual, must have no parameters, and must return 'void' // [ModuleInitializer] - Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(7, 6), - // (8,32): 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. - // internal static async Task M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(8, 32)); + Diagnostic(ErrorCode.ERR_ModuleInitializerMethodMustBeStaticParameterlessVoid, "ModuleInitializer").WithArguments("M").WithLocation(7, 6) + ); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs index 8236a73106c..aea7c81f322 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs @@ -714,10 +714,8 @@ class C CreateCompilation(source).VerifyDiagnostics( // (4,17): error CS1988: Async methods cannot have ref, in or out parameters // D d = async delegate { }; - Diagnostic(ErrorCode.ERR_BadAsyncArgType, "delegate").WithLocation(4, 17), - // (4,17): 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. - // D d = async delegate { }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(4, 17)); + Diagnostic(ErrorCode.ERR_BadAsyncArgType, "delegate").WithLocation(4, 17) + ); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index fd31ba1327d..7a04fd7df3d 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -2111,10 +2111,7 @@ partial class C async partial void M() { } } "; - CreateCompilation(source).VerifyDiagnostics( - // (15,24): 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 partial void M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M")); + CreateCompilation(source).VerifyDiagnostics(); } [WorkItem(910100, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/910100")] @@ -2267,9 +2264,6 @@ static async ref int M() { } // (4,18): error CS1073: Unexpected token 'ref' // static async ref int M() { } Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 18), - // (4,26): 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 ref int M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 26), // (4,26): error CS0161: 'C.M()': not all code paths return a value // static async ref int M() { } Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 26) @@ -2291,9 +2285,6 @@ static async ref readonly int M() { } // (4,18): error CS1073: Unexpected token 'ref' // static async ref readonly int M() { } Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(4, 18), - // (4,35): 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 ref readonly int M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 35), // (4,35): error CS0161: 'C.M()': not all code paths return a value // static async ref readonly int M() { } Diagnostic(ErrorCode.ERR_ReturnExpected, "M").WithArguments("C.M()").WithLocation(4, 35) diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/AnonymousFunctionParsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/AnonymousFunctionParsingTests.cs index 08ed75807b1..986af50abc2 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/AnonymousFunctionParsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/AnonymousFunctionParsingTests.cs @@ -113,10 +113,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (8,26): error CS1004: Duplicate 'async' modifier // Action v = async async delegate() { }; - Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(8, 26), - // (8,32): 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. - // Action v = async async delegate() { }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(8, 32)); + Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(8, 26) + ); } [Fact] @@ -311,10 +309,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (8,20): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Action v = static async delegate() { }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(8, 20), - // (8,33): 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. - // Action v = static async delegate() { }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(8, 33)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(8, 20) + ); } [Fact] @@ -412,10 +408,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (8,20): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Action v = static async delegate() { }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(8, 20), - // (8,33): 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. - // Action v = static async delegate() { }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(8, 33)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(8, 20) + ); } [Fact] @@ -765,10 +759,7 @@ void M1() } EOF(); - CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( - // (9,46): 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. - // Func> v = async async => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 46)); + CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify(); } [Fact] @@ -2578,10 +2569,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (9,34): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Func> v = static async async => async; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 34), - // (9,53): 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. - // Func> v = static async async => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 53)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 34) + ); } [Fact] @@ -2726,10 +2715,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (9,40): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Func> v = async static async => async; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 40), - // (9,53): 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. - // Func> v = async static async => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 53)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 40) + ); } [Fact] @@ -2878,10 +2865,8 @@ void M1() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 40), // (9,47): error CS1004: Duplicate 'async' modifier // Func> v = async static async async => async; - Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(9, 47), - // (9,59): 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. - // Func> v = async static async async => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 59)); + Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(9, 47) + ); } [Fact] @@ -3137,10 +3122,7 @@ void M1() } EOF(); - CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( - // (9,48): 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. - // Func> v = async (async) => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 48)); + CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify(); } [Fact] @@ -3404,10 +3386,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (9,34): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Func> v = static async (async) => async; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 34), - // (9,55): 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. - // Func> v = static async (async) => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 55)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 34) + ); } [Fact] @@ -3557,10 +3537,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (9,40): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Func> v = async static (async) => async; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 40), - // (9,55): 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. - // Func> v = async static (async) => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 55)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 40) + ); } [Fact] @@ -3714,10 +3692,8 @@ void M1() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(9, 40), // (9,47): error CS1004: Duplicate 'async' modifier // Func> v = async static async (async) => async; - Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(9, 47), - // (9,61): 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. - // Func> v = async static async (async) => async; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(9, 61)); + Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(9, 47) + ); } [Fact] @@ -3991,10 +3967,7 @@ void M1() } EOF(); - CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( - // (10,38): 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. - // Func> v = async () => a; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(10, 38)); + CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify(); } [Fact] @@ -4276,10 +4249,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (10,29): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Func> v = static async () => a; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(10, 29), - // (10,45): 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. - // Func> v = static async () => a; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(10, 45)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(10, 29) + ); } [Fact] @@ -4438,10 +4409,8 @@ void M1() CreateCompilation(test, parseOptions: TestOptions.Regular8).GetDiagnostics().Verify( // (10,35): error CS8400: Feature 'static anonymous function' is not available in C# 8.0. Please use language version 9.0 or greater. // Func> v = async static () => a; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(10, 35), - // (10,45): 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. - // Func> v = async static () => a; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(10, 45)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(10, 35) + ); } [Fact] @@ -4604,10 +4573,8 @@ void M1() Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion8, "static").WithArguments("static anonymous function", "9.0").WithLocation(10, 35), // (10,42): error CS1004: Duplicate 'async' modifier // Func> v = async static async () => a; - Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(10, 42), - // (10,51): 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. - // Func> v = async static async () => a; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(10, 51)); + Diagnostic(ErrorCode.ERR_DuplicateModifier, "async").WithArguments("async").WithLocation(10, 42) + ); } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs index a0a35785bfa..21a2604675a 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests_MissingIdentifiers.cs @@ -4209,5 +4209,2816 @@ file struct { } } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Fixed() + { + UsingDeclaration(""" + void M() + { + List + fixed + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1003: Syntax error, '(' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 10), + // (4,10): error CS1031: Type expected + // fixed + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 10), + // (4,10): error CS1001: Identifier expected + // fixed + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 10), + // (4,10): error CS1003: Syntax error, ',' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 10), + // (5,2): error CS1026: ) expected + // } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FixedStatement); + { + N(SyntaxKind.FixedKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Fixed_DoubleGeneric() + { + UsingDeclaration(""" + void M() + { + List> + fixed + } + """, + options: null, + // (3,21): error CS1525: Invalid expression term 'fixed' + // List> + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("fixed").WithLocation(3, 21), + // (3,21): error CS1002: ; expected + // List> + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 21), + // (4,10): error CS1003: Syntax error, '(' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 10), + // (4,10): error CS1031: Type expected + // fixed + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 10), + // (4,10): error CS1001: Identifier expected + // fixed + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 10), + // (4,10): error CS1003: Syntax error, ',' expected + // fixed + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 10), + // (5,2): error CS1026: ) expected + // } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.RightShiftExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanGreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FixedStatement); + { + N(SyntaxKind.FixedKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Break() + { + UsingDeclaration(""" + void M() + { + List + break + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1002: ; expected + // break + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 10)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Continue() + { + UsingDeclaration(""" + void M() + { + List + continue + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,13): error CS1002: ; expected + // continue + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 13)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ContinueStatement); + { + N(SyntaxKind.ContinueKeyword); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Try() + { + UsingDeclaration(""" + void M() + { + List + try + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,8): error CS1514: { expected + // try + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(4, 8), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.TryStatement); + { + N(SyntaxKind.TryKeyword); + N(SyntaxKind.Block); + { + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.FinallyClause); + { + M(SyntaxKind.FinallyKeyword); + M(SyntaxKind.Block); + { + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Do() + { + UsingDeclaration(""" + void M() + { + List + do + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,7): error CS1525: Invalid expression term '}' + // do + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1002: ; expected + // do + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 7), + // (4,7): error CS1003: Syntax error, 'while' expected + // do + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("while").WithLocation(4, 7), + // (4,7): error CS1003: Syntax error, '(' expected + // do + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 7), + // (4,7): error CS1525: Invalid expression term '}' + // do + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1026: ) expected + // do + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 7), + // (4,7): error CS1002: ; expected + // do + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 7)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.DoStatement); + { + N(SyntaxKind.DoKeyword); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.WhileKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_For() + { + UsingDeclaration(""" + void M() + { + List + for + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,8): error CS1003: Syntax error, '(' expected + // for + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 8), + // (4,8): error CS1001: Identifier expected + // for + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 8), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1026: ) expected + // } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(5, 2), + // (5,2): error CS1733: Expected expression + // } + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(5, 2), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ForStatement); + { + N(SyntaxKind.ForKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Foreach() + { + UsingDeclaration(""" + void M() + { + List + foreach + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,12): error CS1003: Syntax error, '(' expected + // foreach + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 12), + // (4,12): error CS1525: Invalid expression term '}' + // foreach + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 12), + // (4,12): error CS1515: 'in' expected + // foreach + Diagnostic(ErrorCode.ERR_InExpected, "").WithLocation(4, 12), + // (4,12): error CS0230: Type and identifier are both required in a foreach statement + // foreach + Diagnostic(ErrorCode.ERR_BadForeachDecl, "").WithLocation(4, 12), + // (4,12): error CS1525: Invalid expression term '}' + // foreach + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 12), + // (4,12): error CS1026: ) expected + // foreach + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 12), + // (4,12): error CS1525: Invalid expression term '}' + // foreach + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 12), + // (4,12): error CS1002: ; expected + // foreach + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 12)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ForEachVariableStatement); + { + N(SyntaxKind.ForEachKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.InKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Goto() + { + UsingDeclaration(""" + void M() + { + List + goto + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,9): error CS1001: Identifier expected + // goto + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 9), + // (4,9): error CS1002: ; expected + // goto + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 9)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.GotoStatement); + { + N(SyntaxKind.GotoKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_If() + { + UsingDeclaration(""" + void M() + { + List + if + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,7): error CS1003: Syntax error, '(' expected + // if + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 7), + // (4,7): error CS1525: Invalid expression term '}' + // if + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1026: ) expected + // if + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 7), + // (4,7): error CS1525: Invalid expression term '}' + // if + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 7), + // (4,7): error CS1002: ; expected + // if + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 7)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Else() + { + UsingDeclaration(""" + void M() + { + List + else + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (3,15): error CS8641: 'else' cannot start a statement. + // List + Diagnostic(ErrorCode.ERR_ElseCannotStartStatement, "").WithLocation(3, 15), + // (3,15): error CS1003: Syntax error, '(' expected + // List + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(3, 15), + // (3,15): error CS1525: Invalid expression term 'else' + // List + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("else").WithLocation(3, 15), + // (3,15): error CS1026: ) expected + // List + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(3, 15), + // (3,15): error CS1525: Invalid expression term 'else' + // List + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("else").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,9): error CS1525: Invalid expression term '}' + // else + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 9), + // (4,9): error CS1002: ; expected + // else + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 9)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.IfStatement); + { + M(SyntaxKind.IfKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ElseClause); + { + N(SyntaxKind.ElseKeyword); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Lock() + { + UsingDeclaration(""" + void M() + { + List + lock + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,9): error CS1003: Syntax error, '(' expected + // lock + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 9), + // (4,9): error CS1525: Invalid expression term '}' + // lock + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 9), + // (4,9): error CS1026: ) expected + // lock + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 9), + // (4,9): error CS1525: Invalid expression term '}' + // lock + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 9), + // (4,9): error CS1002: ; expected + // lock + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 9)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LockStatement); + { + N(SyntaxKind.LockKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Return() + { + UsingDeclaration(""" + void M() + { + List + return + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,11): error CS1525: Invalid expression term '}' + // return + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 11), + // (4,11): error CS1002: ; expected + // return + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 11)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ReturnStatement); + { + N(SyntaxKind.ReturnKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Switch() + { + UsingDeclaration(""" + void M() + { + List + switch + } + """, + options: null, + // (3,15): error CS1525: Invalid expression term 'switch' + // List + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("switch").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,11): error CS1525: Invalid expression term '}' + // switch + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 11), + // (4,11): error CS8515: Parentheses are required around the switch governing expression. + // switch + Diagnostic(ErrorCode.ERR_SwitchGoverningExpressionRequiresParens, "").WithLocation(4, 11), + // (4,11): error CS1514: { expected + // switch + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(4, 11), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Unsafe() + { + UsingDeclaration(""" + void M() + { + List + unsafe + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,5): error CS0106: The modifier 'unsafe' is not valid for this item + // unsafe + Diagnostic(ErrorCode.ERR_BadMemberFlag, "unsafe").WithArguments("unsafe").WithLocation(4, 5), + // (4,11): error CS1031: Type expected + // unsafe + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 11), + // (4,11): error CS1001: Identifier expected + // unsafe + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 11), + // (4,11): error CS1003: Syntax error, ',' expected + // unsafe + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 11), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.UnsafeKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Using() + { + UsingDeclaration(""" + void M() + { + List + using + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1031: Type expected + // using + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 10), + // (4,10): error CS1001: Identifier expected + // using + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 10), + // (4,10): error CS1003: Syntax error, ',' expected + // using + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 10), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.UsingKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_While() + { + UsingDeclaration(""" + void M() + { + List + while + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,10): error CS1003: Syntax error, '(' expected + // while + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(4, 10), + // (4,10): error CS1525: Invalid expression term '}' + // while + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 10), + // (4,10): error CS1026: ) expected + // while + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(4, 10), + // (4,10): error CS1525: Invalid expression term '}' + // while + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(4, 10), + // (4,10): error CS1002: ; expected + // while + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 10)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.WhileStatement); + { + N(SyntaxKind.WhileKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.ExpressionStatement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Volatile() + { + UsingDeclaration(""" + void M() + { + List + volatile + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,5): error CS0106: The modifier 'volatile' is not valid for this item + // volatile + Diagnostic(ErrorCode.ERR_BadMemberFlag, "volatile").WithArguments("volatile").WithLocation(4, 5), + // (4,13): error CS1031: Type expected + // volatile + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 13), + // (4,13): error CS1001: Identifier expected + // volatile + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 13), + // (4,13): error CS1003: Syntax error, ',' expected + // volatile + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 13), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VolatileKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Extern() + { + UsingDeclaration(""" + void M() + { + List + extern + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (4,5): error CS0106: The modifier 'extern' is not valid for this item + // extern + Diagnostic(ErrorCode.ERR_BadMemberFlag, "extern").WithArguments("extern").WithLocation(4, 5), + // (4,11): error CS1031: Type expected + // extern + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 11), + // (4,11): error CS1001: Identifier expected + // extern + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(4, 11), + // (4,11): error CS1003: Syntax error, ',' expected + // extern + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(4, 11), + // (5,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 2), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ExternKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Case() + { + UsingDeclaration(""" + void M() + { + List + case + } + """, + options: null, + // (3,15): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 15), + // (3,15): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 15), + // (3,15): error CS1003: Syntax error, 'switch' expected + // List + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("switch").WithLocation(3, 15), + // (4,9): error CS8504: Pattern missing + // case + Diagnostic(ErrorCode.ERR_MissingPattern, "").WithLocation(4, 9), + // (4,9): error CS1003: Syntax error, ':' expected + // case + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(":").WithLocation(4, 9), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SwitchStatement); + { + M(SyntaxKind.SwitchKeyword); + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + M(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CaseSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.ColonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + [InlineData("checked", SyntaxKind.CheckedExpression, SyntaxKind.CheckedKeyword)] + [InlineData("unchecked", SyntaxKind.UncheckedExpression, SyntaxKind.UncheckedKeyword)] + public void DefiniteStatementAfterGenericType_Checked(string keyword, SyntaxKind expressionKind, SyntaxKind tokenKind) + { + UsingDeclaration($$""" + void M() + { + List {{keyword}}(1); + } + """); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + N(expressionKind); + { + N(tokenKind); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Throw() + { + UsingDeclaration($$""" + void M() + { + List throw ex; + } + """, + options: null, + // (3,16): error CS1525: Invalid expression term 'throw' + // List throw ex; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "throw ex").WithArguments("throw").WithLocation(3, 16)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ex"); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_OpenBrace() + { + UsingDeclaration($$""" + void M() + { + List { + } + """, + options: null, + // (3,16): error CS1002: ; expected + // List { + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(3, 16), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Semicolon() + { + UsingDeclaration($$""" + void M() + { + List; + } + """); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Static() + { + UsingDeclaration($$""" + void M() + { + List static + } + """, + null, + // (3,16): error CS1525: Invalid expression term 'static' + // List static + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "static").WithArguments("static").WithLocation(3, 16), + // (3,16): error CS1002: ; expected + // List static + Diagnostic(ErrorCode.ERR_SemicolonExpected, "static").WithLocation(3, 16), + // (3,16): error CS0106: The modifier 'static' is not valid for this item + // List static + Diagnostic(ErrorCode.ERR_BadMemberFlag, "static").WithArguments("static").WithLocation(3, 16), + // (3,22): error CS1031: Type expected + // List static + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(3, 22), + // (3,22): error CS1001: Identifier expected + // List static + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 22), + // (3,22): error CS1003: Syntax error, ',' expected + // List static + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(3, 22), + // (4,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.StaticKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_ReadOnlyKeyword() + { + UsingDeclaration($$""" + void M() + { + List readonly + } + """, + options: null, + // (3,16): error CS1525: Invalid expression term 'readonly' + // List readonly + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "readonly").WithArguments("readonly").WithLocation(3, 16), + // (3,16): error CS1002: ; expected + // List readonly + Diagnostic(ErrorCode.ERR_SemicolonExpected, "readonly").WithLocation(3, 16), + // (3,16): error CS0106: The modifier 'readonly' is not valid for this item + // List readonly + Diagnostic(ErrorCode.ERR_BadMemberFlag, "readonly").WithArguments("readonly").WithLocation(3, 16), + // (3,24): error CS1031: Type expected + // List readonly + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(3, 24), + // (3,24): error CS1001: Identifier expected + // List readonly + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 24), + // (3,24): error CS1003: Syntax error, ',' expected + // List readonly + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(",").WithLocation(3, 24), + // (4,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.ReadOnlyKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Ref() + { + UsingDeclaration($$""" + void M() + { + List ref + } + """, + options: null, + // (3,16): error CS1525: Invalid expression term 'ref' + // List ref + Diagnostic(ErrorCode.ERR_InvalidExprTerm, """ + ref + + """).WithArguments("ref").WithLocation(3, 16), + // (3,19): error CS1525: Invalid expression term '}' + // List ref + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("}").WithLocation(3, 19), + // (3,19): error CS1002: ; expected + // List ref + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 19)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.GreaterThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "List"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Bracket() + { + UsingDeclaration($$""" + void M() + { + List [ + } + """, + options: null, + // (3,17): error CS1001: Identifier expected + // List [ + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 17), + // (4,2): error CS1003: Syntax error, ']' expected + // } + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("]").WithLocation(4, 2), + // (4,2): error CS1002: ; expected + // } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(4, 2), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_Dot() + { + UsingDeclaration($$""" + void M() + { + List . + } + """, + options: null, + // (3,17): error CS1001: Identifier expected + // List . + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 17), + // (3,17): error CS1002: ; expected + // List . + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 17)); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + [InlineData("class", SyntaxKind.ClassDeclaration, SyntaxKind.ClassKeyword)] + [InlineData("struct", SyntaxKind.StructDeclaration, SyntaxKind.StructKeyword)] + [InlineData("interface", SyntaxKind.InterfaceDeclaration, SyntaxKind.InterfaceKeyword)] + [InlineData("enum", SyntaxKind.EnumDeclaration, SyntaxKind.EnumKeyword)] + public void DefiniteStatementAfterGenericType_TypeDecl(string typeText, SyntaxKind declarationKind, SyntaxKind keywordKind) + { + int column = 1 + typeText.Length; + UsingTree($$""" + List + + {{typeText}} + """, + options: null, + // (1,11): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 11), + // (1,11): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 11), + // (3,7): error CS1001: Identifier expected + // struct + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, column), + // (3,7): error CS1514: { expected + // struct + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(3, column), + // (3,7): error CS1513: } expected + // struct + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(3, column)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(declarationKind); + { + N(keywordKind); + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_RecordDecl() + { + UsingTree($$""" + List + + record + """, + options: null, + // (3,7): error CS1002: ; expected + // record + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 7)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "record"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_RecordDecl2() + { + UsingTree($$""" + List + + record TypeName + """, + options: null, + // (3,8): error CS1003: Syntax error, ',' expected + // record TypeName + Diagnostic(ErrorCode.ERR_SyntaxError, "TypeName").WithArguments(",").WithLocation(3, 8), + // (3,16): error CS1002: ; expected + // record TypeName + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 16)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "record"); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + public void DefiniteStatementAfterGenericType_DelegateDecl() + { + UsingTree($$""" + List + + delegate + """, + options: null, + // (1,11): error CS1001: Identifier expected + // List + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 11), + // (1,11): error CS1002: ; expected + // List + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 11), + // (3,9): error CS1031: Type expected + // delegate + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(3, 9), + // (3,9): error CS1001: Identifier expected + // delegate + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(3, 9), + // (3,9): error CS1003: Syntax error, '(' expected + // delegate + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("(").WithLocation(3, 9), + // (3,9): error CS1026: ) expected + // delegate + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(3, 9), + // (3,9): error CS1002: ; expected + // delegate + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(3, 9)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.DelegateDeclaration); + { + N(SyntaxKind.DelegateKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/79031")] + [InlineData("public", SyntaxKind.PublicKeyword)] + [InlineData("internal", SyntaxKind.InternalKeyword)] + [InlineData("private", SyntaxKind.PrivateKeyword)] + [InlineData("protected", SyntaxKind.ProtectedKeyword)] + public void DefiniteStatementAfterGenericType_Accessibility(string accessibilityText, SyntaxKind accessibilityKind) + { + UsingTree($$""" + List + + {{accessibilityText}} + """, + options: null, + // (3,1): error CS1585: Member modifier 'internal' must precede the member type and name + // internal + Diagnostic(ErrorCode.ERR_BadModifierLocation, accessibilityText).WithArguments(accessibilityText).WithLocation(3, 1), + // (3,1): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // internal + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, accessibilityText).WithLocation(3, 1)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "List"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + N(SyntaxKind.IncompleteMember); + { + N(accessibilityKind); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs index 55c9fc7d560..150061b6944 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs @@ -16,102 +16,68 @@ public class ExtensionsParsingTests : ParsingTests { public ExtensionsParsingTests(ITestOutputHelper output) : base(output) { } - [Fact] - public void LangVer13() + [Theory, CombinatorialData] + public void LangVer_01(bool useCSharp14) { - // Tracked by https://github.com/dotnet/roslyn/issues/78961 : consider giving a LangVer error to trigger UpgradeProject - UsingTree(""" -class C + var src = """ +static class C { extension(object o) where T : struct { } } -""", - TestOptions.Regular13, - // (3,17): error CS1519: Invalid token '(' in class, record, struct, or interface member declaration - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "(").WithArguments("(").WithLocation(3, 17), - // (3,26): error CS8124: Tuple must contain at least two elements. - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(3, 26), - // (3,34): error CS1002: ; expected - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_SemicolonExpected, "T").WithLocation(3, 34), - // (3,36): error CS1519: Invalid token ':' in class, record, struct, or interface member declaration - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ":").WithArguments(":").WithLocation(3, 36), - // (3,36): error CS1519: Invalid token ':' in class, record, struct, or interface member declaration - // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ":").WithArguments(":").WithLocation(3, 36), - // (3,45): error CS1001: Identifier expected +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. // extension(object o) where T : struct { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(3, 45)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension").WithArguments("extensions", "14.0").WithLocation(3, 5)); + UsingTree(src, TestOptions.Regular13); N(SyntaxKind.CompilationUnit); { N(SyntaxKind.ClassDeclaration); { + N(SyntaxKind.StaticKeyword); N(SyntaxKind.ClassKeyword); N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); + N(SyntaxKind.ExtensionBlockDeclaration); { - N(SyntaxKind.GenericName); + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); { - N(SyntaxKind.IdentifierToken, "extension"); - N(SyntaxKind.TypeArgumentList); + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); { - N(SyntaxKind.LessThanToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "T"); - } - N(SyntaxKind.GreaterThanToken); + N(SyntaxKind.IdentifierToken, "T"); } + N(SyntaxKind.GreaterThanToken); } - } - N(SyntaxKind.FieldDeclaration); - { - N(SyntaxKind.VariableDeclaration); + N(SyntaxKind.ParameterList); { - N(SyntaxKind.TupleType); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); { - N(SyntaxKind.OpenParenToken); - N(SyntaxKind.TupleElement); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.ObjectKeyword); - } - N(SyntaxKind.IdentifierToken, "o"); - } - M(SyntaxKind.CommaToken); - M(SyntaxKind.TupleElement); + N(SyntaxKind.PredefinedType); { - M(SyntaxKind.IdentifierName); - { - M(SyntaxKind.IdentifierToken); - } + N(SyntaxKind.ObjectKeyword); } - N(SyntaxKind.CloseParenToken); - } - N(SyntaxKind.VariableDeclarator); - { - N(SyntaxKind.IdentifierToken, "where"); + N(SyntaxKind.IdentifierToken, "o"); } + N(SyntaxKind.CloseParenToken); } - M(SyntaxKind.SemicolonToken); - } - N(SyntaxKind.IncompleteMember); - { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.TypeParameterConstraintClause); { - N(SyntaxKind.IdentifierToken, "T"); + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.StructConstraint); + { + N(SyntaxKind.StructKeyword); + } } - } - N(SyntaxKind.StructDeclaration); - { - N(SyntaxKind.StructKeyword); - M(SyntaxKind.IdentifierToken); N(SyntaxKind.OpenBraceToken); N(SyntaxKind.CloseBraceToken); } @@ -120,23 +86,13 @@ class C N(SyntaxKind.EndOfFileToken); } EOF(); - } - - [Theory, CombinatorialData] - public void LangVer14(bool useCSharp14) - { - UsingTree(""" -class C -{ - extension(object o) where T : struct { } -} -""", - useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); N(SyntaxKind.CompilationUnit); { N(SyntaxKind.ClassDeclaration); { + N(SyntaxKind.StaticKeyword); N(SyntaxKind.ClassKeyword); N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); @@ -188,6 +144,449 @@ class C EOF(); } + [Theory, CombinatorialData] + public void LangVer_02(bool useCSharp14) + { + // Without type parameters + var src = """ +static class C +{ + extension(object o) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. + // extension(object o) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension(object o) { }").WithArguments("extensions", "14.0").WithLocation(3, 5), + // (3,5): error CS0710: Static classes cannot have instance constructors + // extension(object o) { } + Diagnostic(ErrorCode.ERR_ConstructorInStaticClass, "extension").WithLocation(3, 5)); + + UsingTree(src, TestOptions.Regular13); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void LangVer_03() + { + // Without type parameters, escaped identifier + var src = """ +static class C +{ + @extension(object o) { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,5): error CS1520: Method must have a return type + // @extension(object o) { } + Diagnostic(ErrorCode.ERR_MemberNeedsType, "@extension").WithLocation(3, 5), + // (3,5): error CS0710: Static classes cannot have instance constructors + // @extension(object o) { } + Diagnostic(ErrorCode.ERR_ConstructorInStaticClass, "@extension").WithLocation(3, 5)); + + UsingTree(src, TestOptions.Regular13); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "@extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer_04(bool useCSharp14) + { + // Without parameter list + var src = """ +class C +{ + extension { } +} +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (4,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1)); + + UsingTree(src, TestOptions.Regular13, + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (3,15): error CS1519: Invalid token '{' in a member declaration + // extension { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 15), + // (4,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(4, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview, + // (3,15): error CS1003: Syntax error, '(' expected + // extension { } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments("(").WithLocation(3, 15), + // (3,15): error CS1031: Type expected + // extension { } + Diagnostic(ErrorCode.ERR_TypeExpected, "{").WithLocation(3, 15), + // (3,15): error CS1026: ) expected + // extension { } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "{").WithLocation(3, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + M(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + M(SyntaxKind.Parameter); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer_05(bool useCSharp14) + { + // Top-level + var src = """ +extension(object o) { } +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9260: Feature 'extensions' is not available in C# 13.0. Please use language version 14.0 or greater. + // extension(object o) { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion13, "extension").WithArguments("extensions", "14.0").WithLocation(1, 1), + // (1,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object o) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(1, 1)); + + UsingTree(src, TestOptions.Regular13); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer_06(bool useCSharp14) + { + // Top-level, without type parameters + var src = """ +extension(object o) { } +"""; + var comp = CreateCompilation(src, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0103: The name 'extension' does not exist in the current context + // extension(object o) { } + Diagnostic(ErrorCode.ERR_NameNotInContext, "extension").WithArguments("extension").WithLocation(1, 1), + // (1,11): error CS1525: Invalid expression term 'object' + // extension(object o) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "object").WithArguments("object").WithLocation(1, 11), + // (1,18): error CS1003: Syntax error, ',' expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "o").WithArguments(",").WithLocation(1, 18), + // (1,18): error CS0103: The name 'o' does not exist in the current context + // extension(object o) { } + Diagnostic(ErrorCode.ERR_NameNotInContext, "o").WithArguments("o").WithLocation(1, 18), + // (1,21): error CS1002: ; expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 21)); + + UsingTree(src, TestOptions.Regular13, + // (1,11): error CS1525: Invalid expression term 'object' + // extension(object o) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "object").WithArguments("object").WithLocation(1, 11), + // (1,18): error CS1003: Syntax error, ',' expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "o").WithArguments(",").WithLocation(1, 18), + // (1,21): error CS1002: ; expected + // extension(object o) { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(1, 21)); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "o"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(src, useCSharp14 ? TestOptions.Regular14 : TestOptions.RegularPreview); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionBlockDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + [Fact] public void MultipleConstraints() { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs index 4043dbe639c..b84541b3f9c 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/FileModifierParsingTests.cs @@ -1009,9 +1009,6 @@ async file void M() { } // (3,21): error CS0106: The modifier 'file' is not valid for this item // async file void M() { } Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("file").WithLocation(3, 21), - // (3,21): 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 file void M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(3, 21) }); N(SyntaxKind.CompilationUnit); { diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs index 54e8d9ddc16..11142f244eb 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/LocalFunctionParsingTests.cs @@ -1806,44 +1806,26 @@ async static void F2() { } // (5,9): error CS8370: Feature 'static local functions' is not available in C# 7.3. Please use language version 8.0 or greater. // static async void F1() { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "static").WithArguments("static local functions", "8.0").WithLocation(5, 9), - // (5,27): 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 F1() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F1").WithLocation(5, 27), // (5,27): warning CS8321: The local function 'F1' is declared but never used // static async void F1() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F1").WithArguments("F1").WithLocation(5, 27), // (6,15): error CS8370: Feature 'static local functions' is not available in C# 7.3. Please use language version 8.0 or greater. // async static void F2() { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "static").WithArguments("static local functions", "8.0").WithLocation(6, 15), - // (6,27): 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 static void F2() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F2").WithLocation(6, 27), // (6,27): warning CS8321: The local function 'F2' is declared but never used // async static void F2() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F2").WithArguments("F2").WithLocation(6, 27)); CreateCompilation(text, parseOptions: TestOptions.Regular8).VerifyDiagnostics( - // (5,27): 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 F1() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F1").WithLocation(5, 27), // (5,27): warning CS8321: The local function 'F1' is declared but never used // static async void F1() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F1").WithArguments("F1").WithLocation(5, 27), - // (6,27): 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 static void F2() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F2").WithLocation(6, 27), // (6,27): warning CS8321: The local function 'F2' is declared but never used // async static void F2() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F2").WithArguments("F2").WithLocation(6, 27)); CreateCompilation(text, parseOptions: TestOptions.Regular9).VerifyDiagnostics( - // (5,27): 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 F1() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F1").WithLocation(5, 27), // (5,27): warning CS8321: The local function 'F1' is declared but never used // static async void F1() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F1").WithArguments("F1").WithLocation(5, 27), - // (6,27): 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 static void F2() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F2").WithLocation(6, 27), // (6,27): warning CS8321: The local function 'F2' is declared but never used // async static void F2() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F2").WithArguments("F2").WithLocation(6, 27)); @@ -1964,9 +1946,6 @@ static async static void F2() { } // (6,22): error CS8370: Feature 'static local functions' is not available in C# 7.3. Please use language version 8.0 or greater. // static async static void F2() { } Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "static").WithArguments("static local functions", "8.0").WithLocation(6, 22), - // (6,34): 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 static void F2() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F2").WithLocation(6, 34), // (6,34): warning CS8321: The local function 'F2' is declared but never used // static async static void F2() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F2").WithArguments("F2").WithLocation(6, 34)); @@ -1986,9 +1965,6 @@ static async static void F2() { } // (6,22): error CS1004: Duplicate 'static' modifier // static async static void F2() { } Diagnostic(ErrorCode.ERR_DuplicateModifier, "static").WithArguments("static").WithLocation(6, 22), - // (6,34): 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 static void F2() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F2").WithLocation(6, 34), // (6,34): warning CS8321: The local function 'F2' is declared but never used // static async static void F2() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F2").WithArguments("F2").WithLocation(6, 34)); @@ -2008,9 +1984,6 @@ static async static void F2() { } // (6,22): error CS1004: Duplicate 'static' modifier // static async static void F2() { } Diagnostic(ErrorCode.ERR_DuplicateModifier, "static").WithArguments("static").WithLocation(6, 22), - // (6,34): 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 static void F2() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "F2").WithLocation(6, 34), // (6,34): warning CS8321: The local function 'F2' is declared but never used // static async static void F2() { } Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "F2").WithArguments("F2").WithLocation(6, 34)); @@ -2118,7 +2091,7 @@ class Program { void M() { - #pragma warning disable 1998, 8321 + #pragma warning disable 8321 async async void F() { } } } @@ -2195,7 +2168,7 @@ class Program { void M() { - #pragma warning disable 1998, 8321 + #pragma warning disable 8321 async async async void F() { } } } @@ -2279,7 +2252,7 @@ class Program { void M() { - #pragma warning disable 1998, 8321 + #pragma warning disable 8321 async async async async void F() { } } } @@ -2370,7 +2343,7 @@ class Program { void M() { - #pragma warning disable 1998, 8321 + #pragma warning disable 8321 async async async async async void F() { } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index 18c269a58ed..2497d1066f6 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -5119,6 +5119,19 @@ class Test ParseAndValidate(test, Diagnostic(ErrorCode.ERR_GlobalAttributesNotFirst, "assembly")); } + [Fact, WorkItem(863438, "DevDiv/Personal")] + public void CS1730ERR_GlobalAttributesNotFirst2() + { + var test = """ + class Test + { + } + [ assembly :System.Attribute] + """; + + ParseAndValidate(test, Diagnostic(ErrorCode.ERR_GlobalAttributesNotFirst, "assembly")); + } + [Fact(), WorkItem(527039, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/527039")] public void CS1732ERR_ParameterExpected() { @@ -5977,10 +5990,7 @@ async void M() { } var tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); - CreateCompilation(text, parseOptions: TestOptions.Regular5).VerifyDiagnostics( - // (4,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 M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16)); + CreateCompilation(text, parseOptions: TestOptions.Regular5).VerifyDiagnostics(); tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp3)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); @@ -5988,10 +5998,8 @@ async void M() { } CreateCompilation(text, parseOptions: TestOptions.Regular3).VerifyDiagnostics( // (4,16): error CS8024: Feature 'async function' is not available in C# 3. Please use language version 5 or greater. // async void M() { } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion3, "M").WithArguments("async function", "5").WithLocation(4, 16), - // (4,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 M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 16)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion3, "M").WithArguments("async function", "5").WithLocation(4, 16) + ); } [Fact, WorkItem(529870, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529870")] @@ -6005,20 +6013,15 @@ async static void M() { } "; var tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); - CreateCompilation(text, parseOptions: TestOptions.Regular5).VerifyDiagnostics( - // (4,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. - // async static void M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 23)); + CreateCompilation(text, parseOptions: TestOptions.Regular5).VerifyDiagnostics(); tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp3)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); CreateCompilation(text, parseOptions: TestOptions.Regular3).VerifyDiagnostics( // (4,23): error CS8024: Feature 'async function' is not available in C# 3. Please use language version 5 or greater. // async static void M() { } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion3, "M").WithArguments("async function", "5").WithLocation(4, 23), - // (4,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. - // async static void M() { } - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "M").WithLocation(4, 23)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion3, "M").WithArguments("async function", "5").WithLocation(4, 23) + ); } [Fact] @@ -6041,10 +6044,8 @@ static void Main() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Func>").WithArguments("Func<,>").WithLocation(6, 9), // (6,19): error CS0246: The type or namespace name 'Task<>' could not be found (are you missing a using directive or an assembly reference?) // Func> f = async x => x; - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Task").WithArguments("Task<>").WithLocation(6, 19), - // (6,42): 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. - // Func> f = async x => x; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(6, 42)); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Task").WithArguments("Task<>").WithLocation(6, 19) + ); tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp4)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); @@ -6057,10 +6058,8 @@ static void Main() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Task").WithArguments("Task<>").WithLocation(6, 19), // (6,34): error CS8025: Feature 'async function' is not available in C# 4. Please use language version 5 or greater. // Func> f = async x => x; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion4, "async").WithArguments("async function", "5").WithLocation(6, 34), - // (6,42): 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. - // Func> f = async x => x; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "=>").WithLocation(6, 42)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion4, "async").WithArguments("async function", "5").WithLocation(6, 34) + ); } [Fact] @@ -6083,10 +6082,8 @@ static void Main() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Func>").WithArguments("Func<,>").WithLocation(6, 9), // (6,19): error CS0246: The type or namespace name 'Task<>' could not be found (are you missing a using directive or an assembly reference?) // Func> f = async delegate (int x) { return x; }; - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Task").WithArguments("Task<>").WithLocation(6, 19), - // (6,40): 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. - // Func> f = async delegate (int x) { return x; }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(6, 40)); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Task").WithArguments("Task<>").WithLocation(6, 19) + ); tree = SyntaxFactory.ParseSyntaxTree(text, options: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp4)); tree.GetCompilationUnitRoot().GetDiagnostics().Verify(); @@ -6099,10 +6096,8 @@ static void Main() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Task").WithArguments("Task<>").WithLocation(6, 19), // (6,34): error CS8025: Feature 'async function' is not available in C# 4. Please use language version 5 or greater. // Func> f = async delegate (int x) { return x; }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion4, "async").WithArguments("async function", "5").WithLocation(6, 34), - // (6,40): 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. - // Func> f = async delegate (int x) { return x; }; - Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "delegate").WithLocation(6, 40)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion4, "async").WithArguments("async function", "5").WithLocation(6, 34) + ); } [Fact] diff --git a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs index cde011bb78e..b05dc0f3771 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs @@ -6197,5 +6197,46 @@ void M6() } """); } + + [Fact] + public void TestNormalizeExtension_01() + { + TestNormalizeDeclaration(""" +static class E +{ +extension < T > ( int i ) where T : struct +{ +} +} +""", """ +static class E +{ + extension(int i) + where T : struct + { + } +} +"""); + } + + [Fact] + public void TestNormalizeExtension_02() + { + TestNormalizeDeclaration(""" +static class E +{ +extension ( int ) +{ +} +} +""", """ +static class E +{ + extension(int) + { + } +} +"""); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj b/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj index 9ab6763f608..5708cc5a911 100644 --- a/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj +++ b/src/roslyn/src/Compilers/CSharp/csc/AnyCpu/csc.csproj @@ -4,8 +4,8 @@ Exe $(NetRoslynSourceBuild);net472 - false true + false diff --git a/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs b/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs index 9848453306a..6c9685563c0 100644 --- a/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs +++ b/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Diagnostics/DiagnosticLocalizationTests.cs @@ -304,11 +304,21 @@ private static void TestDescriptorIsExceptionSafeCore(DiagnosticDescriptor descr Action onAnalyzerException = (ex, a, diag, ct) => exceptionDiagnostics.Add(diag); var analyzerManager = new AnalyzerManager(analyzer); var compilation = CSharp.CSharpCompilation.Create("test"); - var analyzerExecutor = AnalyzerExecutor.Create(compilation, AnalyzerOptions.Empty, - addNonCategorizedDiagnostic: (_, _) => { }, onAnalyzerException, analyzerExceptionFilter: null, - isCompilerAnalyzer: _ => false, analyzerManager, shouldSkipAnalysisOnGeneratedCode: _ => false, - shouldSuppressGeneratedCodeDiagnostic: (_, _, _, _) => false, isGeneratedCodeLocation: (_, _, _) => false, - isAnalyzerSuppressedForTree: (_, _, _, _) => false, getAnalyzerGate: _ => null, + var analyzerExecutor = AnalyzerExecutor.Create( + compilation, + AnalyzerOptions.Empty, + addNonCategorizedDiagnostic: (_, _, _) => { }, + onAnalyzerException, + analyzerExceptionFilter: null, + isCompilerAnalyzer: _ => false, + diagnosticAnalyzers: [analyzer], + getAnalyzerConfigOptionsProvider: null, + analyzerManager, + shouldSkipAnalysisOnGeneratedCode: _ => false, + shouldSuppressGeneratedCodeDiagnostic: (_, _, _, _) => false, + isGeneratedCodeLocation: (_, _, _) => false, + isAnalyzerSuppressedForTree: (_, _, _, _) => false, + getAnalyzerGate: _ => null, getSemanticModel: tree => compilation.GetSemanticModel(tree, ignoreAccessibility: true), SeverityFilter.None); var descriptors = analyzerManager.GetSupportedDiagnosticDescriptors(analyzer, analyzerExecutor, CancellationToken.None); diff --git a/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextStreamTests.cs b/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextStreamTests.cs index 6d7a05ce6d1..1fff5067dcb 100644 --- a/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextStreamTests.cs +++ b/src/roslyn/src/Compilers/Core/CodeAnalysisTest/Text/SourceTextStreamTests.cs @@ -44,11 +44,11 @@ public void MinimumLength() [Fact] public void Issue1197() { - var baseText = "food time"; + var baseText = new string('a', SourceTextStream.BufferSize - 1); var text = string.Format("{0}{1}", baseText, '\u2019'); var encoding = s_utf8NoBom; var sourceText = SourceText.From(text, encoding); - using (var stream = new SourceTextStream(sourceText, bufferSize: text.Length * 2)) + using (var stream = new SourceTextStream(sourceText)) { var buffer = new byte[baseText.Length + 1]; Assert.Equal(baseText.Length, stream.Read(buffer, 0, buffer.Length)); diff --git a/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs b/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs index c26a3cb0f2d..9d1229e43be 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTask/InteractiveCompiler.cs @@ -16,8 +16,6 @@ namespace Microsoft.CodeAnalysis.BuildTasks /// public abstract class InteractiveCompiler : ManagedToolTask { - internal readonly PropertyDictionary _store = new PropertyDictionary(); - public InteractiveCompiler() : base(ErrorString.ResourceManager) { diff --git a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index 89e34f57541..d7e52b89bd5 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -51,7 +51,6 @@ private enum CompilationKind } private CancellationTokenSource? _sharedCompileCts; - internal readonly PropertyDictionary _store = new PropertyDictionary(); internal abstract RequestLanguage Language { get; } diff --git a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs index 6fb01d315c4..58c3450e683 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTask/ManagedToolTask.cs @@ -10,12 +10,16 @@ using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Microsoft.CodeAnalysis.CommandLine; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.BuildTasks { public abstract class ManagedToolTask : ToolTask { + private bool? _useAppHost; + internal readonly PropertyDictionary _store = new PropertyDictionary(); + /// /// A copy of this task, compiled for .NET Framework, is deployed into the .NET SDK. It is a bridge task /// that is loaded into .NET Framework MSBuild but launches the .NET Core compiler. This task necessarily @@ -53,6 +57,23 @@ public abstract class ManagedToolTask : ToolTask internal string PathToBuiltInTool => Path.Combine(GetToolDirectory(), ToolName); + /// + /// We fallback to not use the apphost if it is not present (can happen in compiler toolset scenarios for example). + /// + private bool UseAppHost + { + get + { + if (_useAppHost is not { } useAppHost) + { + _useAppHost = useAppHost = File.Exists(Path.Combine(GetToolDirectory(), AppHostToolName)); + Debug.Assert(IsBuiltinToolRunningOnCoreClr || useAppHost); + } + + return useAppHost; + } + } + protected ManagedToolTask(ResourceManager resourceManager) : base(resourceManager) { @@ -78,7 +99,8 @@ internal string GenerateToolArguments() protected sealed override string GenerateCommandLineCommands() { var commandLineArguments = GenerateToolArguments(); - if (UsingBuiltinTool && IsBuiltinToolRunningOnCoreClr) + + if (UsingBuiltinTool && !UseAppHost) { commandLineArguments = RuntimeHostInfo.GetDotNetExecCommandLine(PathToBuiltInTool, commandLineArguments); } @@ -115,15 +137,17 @@ protected sealed override string GenerateResponseFileCommands() /// /// This generates the path to the executable that is directly ran. - /// This could be the managed assembly itself (on desktop .NET on Windows), - /// or a runtime such as dotnet. + /// This could be the executable apphost or a runtime such as dotnet. /// - protected sealed override string GenerateFullPathToTool() => (UsingBuiltinTool, IsBuiltinToolRunningOnCoreClr) switch + protected sealed override string GenerateFullPathToTool() { - (true, true) => RuntimeHostInfo.GetDotNetPathOrDefault(), - (true, false) => PathToBuiltInTool, - (false, _) => Path.Combine(ToolPath ?? "", ToolExe) - }; + if (UsingBuiltinTool) + { + return UseAppHost ? PathToBuiltInTool : RuntimeHostInfo.GetDotNetPathOrDefault(); + } + + return Path.Combine(ToolPath ?? "", ToolExe); + } protected abstract string ToolNameWithoutExtension { get; } @@ -139,10 +163,15 @@ protected sealed override string GenerateResponseFileCommands() /// It returns the name of the managed assembly, which might not be the path returned by /// GenerateFullPathToTool, which can return the path to e.g. the dotnet executable. /// - protected sealed override string ToolName => - IsBuiltinToolRunningOnCoreClr - ? $"{ToolNameWithoutExtension}.dll" - : $"{ToolNameWithoutExtension}.exe"; + protected sealed override string ToolName + { + get + { + return UseAppHost ? AppHostToolName : $"{ToolNameWithoutExtension}.dll"; + } + } + + private string AppHostToolName => $"{ToolNameWithoutExtension}{PlatformInformation.ExeExtension}"; /// /// This generates the command line arguments passed to the tool. @@ -206,5 +235,17 @@ internal static string GetBuildTaskDirectory() return buildTaskDirectory; } + + protected override bool ValidateParameters() + { + // Set DOTNET_ROOT so that the apphost executables launch properly. + if (RuntimeHostInfo.GetToolDotNetRoot() is { } dotNetRoot) + { + Log.LogMessage("Setting {0} to '{1}'", RuntimeHostInfo.DotNetRootEnvironmentName, dotNetRoot); + EnvironmentVariables = [.. EnvironmentVariables ?? [], $"{RuntimeHostInfo.DotNetRootEnvironmentName}={dotNetRoot}"]; + } + + return base.ValidateParameters(); + } } } diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs index 8325b71fcf0..c59e16bdd28 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/CscTests.cs @@ -6,6 +6,7 @@ using System.IO; using Microsoft.CodeAnalysis.BuildTasks.UnitTests.TestUtilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -484,14 +485,13 @@ public void EmptyCscToolExe() csc.ToolExe = ""; csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); Assert.Equal("", csc.GenerateCommandLineContents()); - // StartsWith because it can be csc.exe or csc.dll - Assert.StartsWith(Path.Combine("path", "to", "custom_csc", "csc."), csc.GeneratePathToTool()); + AssertEx.Equal(Path.Combine("path", "to", "custom_csc", $"csc{PlatformInformation.ExeExtension}"), csc.GeneratePathToTool()); csc = new Csc(); csc.ToolPath = Path.Combine("path", "to", "custom_csc"); csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); Assert.Equal("", csc.GenerateCommandLineContents()); - Assert.StartsWith(Path.Combine("path", "to", "custom_csc", "csc."), csc.GeneratePathToTool()); + AssertEx.Equal(Path.Combine("path", "to", "custom_csc", $"csc{PlatformInformation.ExeExtension}"), csc.GeneratePathToTool()); } [Fact] diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs index cfebef67d98..c85de16d2f2 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/IntegrationTests.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. -#if NET472 using System.Collections.Generic; using System.IO; using Microsoft.CodeAnalysis.Test.Utilities; @@ -24,24 +23,6 @@ private ProcessResult RunCompilerOutput(TempFile file) return ProcessUtilities.Run(file.Path, "", Path.GetDirectoryName(file.Path)); } - private static void VerifyResult(ProcessResult result) - { - Assert.Equal("", result.Output); - Assert.Equal("", result.Errors); - Assert.Equal(0, result.ExitCode); - } - - private void VerifyResultAndOutput(ProcessResult result, TempDirectory path, string expectedOutput) - { - using (var resultFile = GetResultFile(path, "hello.exe")) - { - VerifyResult(result); - - var runningResult = RunCompilerOutput(resultFile); - Assert.Equal(expectedOutput, runningResult.Output); - } - } - // A dictionary with name and contents of all the files we want to create for the SimpleMSBuild test. private Dictionary SimpleMsBuildFiles => new Dictionary { { "HelloSolution.sln", @@ -536,7 +517,7 @@ public void ReportAnalyzerMSBuild() string arguments = string.Format(@"/m /nr:false /t:Rebuild /p:UseSharedCompilation=false /p:UseRoslyn=1 HelloSolution.sln"); var result = RunCommandLineCompiler(_msbuildExecutable, arguments, _tempDirectory, ReportAnalyzerMsBuildFiles, new Dictionary - { { "MyMSBuildToolsPath", Path.GetDirectoryName(typeof(IntegrationTests).Assembly.Location) } }); + { { "MyMSBuildToolsPath", Path.GetDirectoryName(typeof(IntegrationTests).Assembly.Location)! } }); Assert.True(result.ExitCode != 0); Assert.Contains("/reportanalyzer", result.Output); @@ -717,4 +698,3 @@ public class Class1 } } } -#endif diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs index 4e69936b4e6..3a55c89cf10 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/MSBuildManagedToolTests.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.IO; -using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests; @@ -16,7 +15,7 @@ public void PathToBuiltinTool() { var taskPath = Path.GetDirectoryName(typeof(ManagedCompiler).Assembly.Location)!; var relativePath = RuntimeHostInfo.IsCoreClrRuntime - ? Path.Combine("bincore", "csc.dll") + ? Path.Combine("bincore", $"csc{PlatformInformation.ExeExtension}") : "csc.exe"; var task = new Csc(); Assert.Equal(Path.Combine(taskPath, relativePath), task.PathToBuiltInTool); diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj index c4e50a85580..be45ea70de1 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/Microsoft.Build.Tasks.CodeAnalysis.UnitTests.csproj @@ -69,6 +69,7 @@ <_CommandLineCompilerReferenceOutputPath>@(CommandLineCompilerReference->'%(RootDir)%(Directory)*.*') + <_LinkPrefix Condition="'$(TargetFramework)' != 'net472'">bincore\ <_CommandLineCompilerReferenceContent Include="$(_CommandLineCompilerReferenceOutputPath)" /> @@ -78,7 +79,7 @@ - + diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs index a2af1e8b354..7be928d9fe0 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/IntegrationTestBase.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. -#if NET472 - -using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -19,10 +16,12 @@ public abstract class IntegrationTestBase : TestBase { protected static readonly string? s_msbuildDirectory; +#if NET472 static IntegrationTestBase() { s_msbuildDirectory = DesktopTestHelpers.GetMSBuildDirectory(); } +#endif protected readonly ITestOutputHelper _output; protected readonly string? _msbuildExecutable; @@ -89,6 +88,105 @@ protected static ProcessResult RunCommandLineCompiler( additionalEnvironmentVars: AddForLoggingEnvironmentVars(additionalEnvironmentVars)); } + protected ProcessResult? RunMsbuild( + string arguments, + TempDirectory currentDirectory, + IEnumerable> filesInDirectory, + IEnumerable>? additionalEnvironmentVars = null) + { + if (_msbuildExecutable != null) + { + return RunCommandLineCompiler( + _msbuildExecutable, + arguments, + currentDirectory, + filesInDirectory, + additionalEnvironmentVars); + } + + if (ExecutionConditionUtil.IsDesktop) + { + _output.WriteLine("Skipping because Framework MSBuild is missing, this is a desktop test, " + + "and we cannot use the desktop Csc/Vbc task from 'dotnet msbuild', i.e., Core MSBuild."); + return null; + } + + return RunCommandLineCompiler( + "dotnet", + $"msbuild {arguments}", + currentDirectory, + filesInDirectory, + additionalEnvironmentVars); + } + + [Theory, CombinatorialData] + public void SdkBuild_Csc(bool useSharedCompilation) + { + var result = RunMsbuild( + "/v:n /m /nr:false /t:Build /restore Test.csproj", + _tempDirectory, + new Dictionary + { + { "File.cs", """ + class Program { static void Main() { System.Console.WriteLine("Hello from file"); } } + """ }, + { "Test.csproj", $""" + + + + netstandard2.0 + {useSharedCompilation} + + + """ }, + }); + + if (result == null) return; + + _output.WriteLine(result.Output); + + Assert.Equal(0, result.ExitCode); + Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output); + Assert.DoesNotContain("csc.dll", result.Output); + Assert.Contains(ExecutionConditionUtil.IsWindows ? "csc.exe" : "csc", result.Output); + } + + [Theory, CombinatorialData] + public void SdkBuild_Vbc(bool useSharedCompilation) + { + var result = RunMsbuild( + "/v:n /m /nr:false /t:Build /restore Test.vbproj", + _tempDirectory, + new Dictionary + { + { "File.vb", """ + Public Module Program + Public Sub Main() + System.Console.WriteLine("Hello from file") + End Sub + End Module + """ }, + { "Test.vbproj", $""" + + + + netstandard2.0 + {useSharedCompilation} + + + """ }, + }); + + if (result == null) return; + + _output.WriteLine(result.Output); + + Assert.Equal(0, result.ExitCode); + Assert.Contains(useSharedCompilation ? "server processed compilation" : "using command line tool by design", result.Output); + Assert.DoesNotContain("vbc.dll", result.Output); + Assert.Contains(ExecutionConditionUtil.IsWindows ? "vbc.exe" : "vbc", result.Output); + } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/79907")] public void StdLib_Csc(bool useSharedCompilation, bool disableSdkPath) { @@ -170,5 +268,3 @@ End Module } } } - -#endif diff --git a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs index c5cab3e2671..617c848f16a 100644 --- a/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs +++ b/src/roslyn/src/Compilers/Core/MSBuildTaskTests/TestUtilities/TaskTestUtil.cs @@ -26,7 +26,7 @@ public static void AssertCommandLine( Assert.Equal(expected, task.GenerateCommandLineArgsTaskItems(rsp).Select(x => x.ItemSpec)); #if NET - Assert.Equal($"exec \"{task.PathToBuiltInTool}\"", task.GenerateCommandLineContents().Trim()); + Assert.Empty(task.GenerateCommandLineContents().Trim()); // Can only run the Execute path on .NET Core presently. The internal workings of ToolTask // will fail if it can't find the tool exe and we don't have csc.exe, vbc.exe, etc ... @@ -40,7 +40,7 @@ public static void AssertCommandLine( var message = engine.BuildMessages.OfType().Single(); var commandLine = message.CommandLine.Replace(" ", " ").Trim(); - Assert.Equal($@"{RuntimeHostInfo.GetDotNetPathOrDefault()} exec ""{task.PathToBuiltInTool}"" {line}", commandLine); + AssertEx.Equal($@"{task.PathToBuiltInTool} {line}", commandLine); compilerTask.NoConfig = true; Assert.Equal("/noconfig", compilerTask.GenerateToolArguments()); diff --git a/src/roslyn/src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.SectionNameMatching.cs b/src/roslyn/src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.SectionNameMatching.cs index 674cbd93964..df83e723869 100644 --- a/src/roslyn/src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.SectionNameMatching.cs +++ b/src/roslyn/src/Compilers/Core/Portable/CommandLine/AnalyzerConfig.SectionNameMatching.cs @@ -123,7 +123,8 @@ public bool IsMatch(string s) internal static string UnescapeSectionName(string sectionName) { - var sb = new StringBuilder(); + var pooledStrbuilder = PooledStringBuilder.GetInstance(); + StringBuilder sb = pooledStrbuilder.Builder; SectionNameLexer lexer = new SectionNameLexer(sectionName); while (!lexer.IsDone) { @@ -139,7 +140,7 @@ internal static string UnescapeSectionName(string sectionName) throw ExceptionUtilities.UnexpectedValue(tokenKind); } } - return sb.ToString(); + return pooledStrbuilder.ToStringAndFree(); } /// diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs index d7f251955cd..938087e5f90 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisScope.cs @@ -95,20 +95,19 @@ internal class AnalysisScope public static AnalysisScope Create(Compilation compilation, ImmutableArray analyzers, CompilationWithAnalyzers compilationWithAnalyzers) { - var analyzerOptions = compilationWithAnalyzers.AnalysisOptions.Options; + var additionalFiles = compilationWithAnalyzers.AnalysisOptions.Options.GetAdditionalFiles(); var hasAllAnalyzers = ComputeHasAllAnalyzers(analyzers, compilationWithAnalyzers); var concurrentAnalysis = compilationWithAnalyzers.AnalysisOptions.ConcurrentAnalysis; - return Create(compilation, analyzerOptions, analyzers, hasAllAnalyzers, concurrentAnalysis); + return Create(compilation, additionalFiles, analyzers, hasAllAnalyzers, concurrentAnalysis); } - public static AnalysisScope CreateForBatchCompile(Compilation compilation, AnalyzerOptions analyzerOptions, ImmutableArray analyzers) + public static AnalysisScope CreateForBatchCompile(Compilation compilation, ImmutableArray additionalFiles, ImmutableArray analyzers) { - return Create(compilation, analyzerOptions, analyzers, hasAllAnalyzers: true, concurrentAnalysis: compilation.Options.ConcurrentBuild); + return Create(compilation, additionalFiles, analyzers, hasAllAnalyzers: true, concurrentAnalysis: compilation.Options.ConcurrentBuild); } - private static AnalysisScope Create(Compilation compilation, AnalyzerOptions? analyzerOptions, ImmutableArray analyzers, bool hasAllAnalyzers, bool concurrentAnalysis) + private static AnalysisScope Create(Compilation compilation, ImmutableArray additionalFiles, ImmutableArray analyzers, bool hasAllAnalyzers, bool concurrentAnalysis) { - var additionalFiles = analyzerOptions?.AdditionalFiles ?? ImmutableArray.Empty; return new AnalysisScope(compilation.CommonSyntaxTrees, additionalFiles, analyzers, hasAllAnalyzers, filterFile: null, filterSpanOpt: null, originalFilterFile: null, originalFilterSpan: null, isSyntacticSingleFileAnalysis: false, diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 84e42332b22..385e075a73c 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -35,6 +35,11 @@ internal abstract partial class AnalyzerDriver : IDisposable private static readonly Func s_IsCompilerAnalyzerFunc = IsCompilerAnalyzer; private static readonly Func s_getTopmostNodeForAnalysis = GetTopmostNodeForAnalysis; + /// + /// Separate pool for diagnostic analyzers as these collections commonly exceed ArrayBuilder's size threshold + /// + private static readonly ObjectPool> s_diagnosticAnalyzerPool = new ObjectPool>(() => new ArrayBuilder()); + private readonly Func _isGeneratedCode; /// @@ -471,23 +476,29 @@ internal void Initialize( var diagnosticQueue = DiagnosticQueue.Create(categorizeDiagnostics); var suppressedDiagnosticIds = trackSuppressedDiagnosticIds ? new ConcurrentSet() : null; - Action? addNotCategorizedDiagnostic = null; - Action? addCategorizedLocalDiagnostic = null; - Action? addCategorizedNonLocalDiagnostic = null; + Action? addNotCategorizedDiagnostic = null; + Action? addCategorizedLocalDiagnostic = null; + Action? addCategorizedNonLocalDiagnostic = null; if (categorizeDiagnostics) { - addCategorizedLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds); - addCategorizedNonLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds); + addCategorizedLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueLocal, compilation, _severityFilter, suppressedDiagnosticIds); + addCategorizedNonLocalDiagnostic = GetDiagnosticSink(diagnosticQueue.EnqueueNonLocal, compilation, _severityFilter, suppressedDiagnosticIds); } else { - addNotCategorizedDiagnostic = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds); + addNotCategorizedDiagnostic = GetDiagnosticSink(diagnosticQueue.Enqueue, compilation, _severityFilter, suppressedDiagnosticIds); } // Wrap onAnalyzerException to pass in filtered diagnostic. - Action newOnAnalyzerException = (ex, analyzer, diagnostic, cancellationToken) => + var options = analysisOptions.Options ?? AnalyzerOptions.Empty; + + var newOnAnalyzerException = (Exception ex, DiagnosticAnalyzer analyzer, Diagnostic diagnostic, CancellationToken cancellationToken) => { - var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analysisOptions.Options, _severityFilter, suppressedDiagnosticIds, cancellationToken); + // Note: in this callback, it's fine/correct to use analysisOptions.Options instead of any diagnostic analyzer + // specific options. That's because the options are only used for filtering/determining-severities. But we + // do not allow analyzers to control the filtering/severity around the reporting of analyzer exceptions themselves. + // These are always passed through and reported to the user. + var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, options, _severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) { if (analysisOptions.OnAnalyzerException != null) @@ -496,18 +507,19 @@ internal void Initialize( } else if (categorizeDiagnostics) { - addCategorizedNonLocalDiagnostic!(filteredDiagnostic, analyzer, cancellationToken); + addCategorizedNonLocalDiagnostic!(filteredDiagnostic, analyzer, options, cancellationToken); } else { - addNotCategorizedDiagnostic!(filteredDiagnostic, cancellationToken); + addNotCategorizedDiagnostic!(filteredDiagnostic, options, cancellationToken); } } }; var analyzerExecutor = AnalyzerExecutor.Create( - compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addNotCategorizedDiagnostic, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, - IsCompilerAnalyzer, AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, IsAnalyzerSuppressedForTree, GetAnalyzerGate, + compilation, options, addNotCategorizedDiagnostic, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, + IsCompilerAnalyzer, analysisScope.Analyzers, analysisOptions.GetAnalyzerConfigOptionsProvider, + AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, IsAnalyzerSuppressedForTree, GetAnalyzerGate, getSemanticModel: GetOrCreateSemanticModel, _severityFilter, analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, s => _programmaticSuppressions!.Add(s)); @@ -847,7 +859,7 @@ internal static AnalyzerDriver CreateAndAttachToCompilation( var categorizeDiagnostics = false; var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, analyzerExceptionFilter: analyzerExceptionFilter, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer, reportSuppressedDiagnostics: false); - var analysisScope = AnalysisScope.CreateForBatchCompile(newCompilation, options, analyzers); + var analysisScope = AnalysisScope.CreateForBatchCompile(newCompilation, options.GetAdditionalFiles(), analyzers); analyzerDriver.Initialize(newCompilation, analysisOptions, new CompilationData(newCompilation), analysisScope, categorizeDiagnostics, trackSuppressedDiagnosticIds, cancellationToken); analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, usingPrePopulatedEventQueue: false, cancellationToken); @@ -1823,7 +1835,7 @@ private bool TryExecuteSymbolEndActions( } var success = true; - var completedAnalyzers = ArrayBuilder.GetInstance(); + ArrayBuilder completedAnalyzers = s_diagnosticAnalyzerPool.Allocate(); var processedAnalyzers = PooledHashSet.GetInstance(); try { @@ -1876,7 +1888,12 @@ private bool TryExecuteSymbolEndActions( finally { processedAnalyzers.Free(); - completedAnalyzers.Free(); + + // Do not call completedAnalyzers.Free, as the ArrayBuilder isn't associated with our pool and even if it were, we don't + // want the default freeing behavior of limiting pooled array size to ArrayBuilder.PooledArrayLengthLimitExclusive. + // Instead, we need to explicitly add this item back to our pool. + completedAnalyzers.Clear(); + s_diagnosticAnalyzerPool.Free(completedAnalyzers); } } @@ -1952,9 +1969,9 @@ private void ExecuteCompilationActions( } } - internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) + internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) { - return (diagnostic, cancellationToken) => + return (diagnostic, analyzerOptions, cancellationToken) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) @@ -1964,9 +1981,9 @@ internal static Action GetDiagnosticSink(Action GetDiagnosticSink(Action addLocalDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) + internal static Action GetDiagnosticSink(Action addLocalDiagnosticCore, Compilation compilation, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) { - return (diagnostic, analyzer, isSyntaxDiagnostic, cancellationToken) => + return (diagnostic, analyzer, analyzerOptions, isSyntaxDiagnostic, cancellationToken) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) @@ -1976,9 +1993,9 @@ internal static Action }; } - internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, AnalyzerOptions? analyzerOptions, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) + internal static Action GetDiagnosticSink(Action addDiagnosticCore, Compilation compilation, SeverityFilter severityFilter, ConcurrentSet? suppressedDiagnosticIds) { - return (diagnostic, analyzer, cancellationToken) => + return (diagnostic, analyzer, analyzerOptions, cancellationToken) => { var filteredDiagnostic = GetFilteredDiagnostic(diagnostic, compilation, analyzerOptions, severityFilter, suppressedDiagnosticIds, cancellationToken); if (filteredDiagnostic != null) diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs index f8730fa78dc..aa593ed94e3 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.AnalyzerDiagnosticReporter.cs @@ -29,10 +29,11 @@ public static AnalyzerDiagnosticReporter GetInstance( TextSpan? span, Compilation compilation, DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, bool isSyntaxDiagnostic, - Action? addNonCategorizedDiagnostic, - Action? addCategorizedLocalDiagnostic, - Action? addCategorizedNonLocalDiagnostic, + Action? addNonCategorizedDiagnostic, + Action? addCategorizedLocalDiagnostic, + Action? addCategorizedNonLocalDiagnostic, Func shouldSuppressGeneratedCodeDiagnostic, CancellationToken cancellationToken) { @@ -41,6 +42,7 @@ public static AnalyzerDiagnosticReporter GetInstance( item.FilterSpanForLocalDiagnostics = span; item._compilation = compilation; item._analyzer = analyzer; + item._analyzerOptions = analyzerOptions; item._isSyntaxDiagnostic = isSyntaxDiagnostic; item._addNonCategorizedDiagnostic = addNonCategorizedDiagnostic; item._addCategorizedLocalDiagnostic = addCategorizedLocalDiagnostic; @@ -56,6 +58,7 @@ public void Free() FilterSpanForLocalDiagnostics = null; _compilation = null!; _analyzer = null!; + _analyzerOptions = null!; _isSyntaxDiagnostic = default; _addNonCategorizedDiagnostic = null!; _addCategorizedLocalDiagnostic = null!; @@ -68,10 +71,11 @@ public void Free() private SourceOrAdditionalFile? _contextFile; private Compilation _compilation; private DiagnosticAnalyzer _analyzer; + private AnalyzerOptions _analyzerOptions; private bool _isSyntaxDiagnostic; - private Action? _addNonCategorizedDiagnostic; - private Action? _addCategorizedLocalDiagnostic; - private Action? _addCategorizedNonLocalDiagnostic; + private Action? _addNonCategorizedDiagnostic; + private Action? _addCategorizedLocalDiagnostic; + private Action? _addCategorizedNonLocalDiagnostic; private Func _shouldSuppressGeneratedCodeDiagnostic; private CancellationToken _cancellationToken; @@ -102,7 +106,7 @@ private void AddDiagnostic(Diagnostic diagnostic) if (_addCategorizedLocalDiagnostic == null) { Debug.Assert(_addNonCategorizedDiagnostic != null); - _addNonCategorizedDiagnostic(diagnostic, _cancellationToken); + _addNonCategorizedDiagnostic(diagnostic, _analyzerOptions, _cancellationToken); return; } @@ -112,11 +116,11 @@ private void AddDiagnostic(Diagnostic diagnostic) if (isLocalDiagnostic(diagnostic) && (!FilterSpanForLocalDiagnostics.HasValue || FilterSpanForLocalDiagnostics.Value.IntersectsWith(diagnostic.Location.SourceSpan))) { - _addCategorizedLocalDiagnostic(diagnostic, _analyzer, _isSyntaxDiagnostic, _cancellationToken); + _addCategorizedLocalDiagnostic(diagnostic, _analyzer, _analyzerOptions, _isSyntaxDiagnostic, _cancellationToken); } else { - _addCategorizedNonLocalDiagnostic(diagnostic, _analyzer, _cancellationToken); + _addCategorizedNonLocalDiagnostic(diagnostic, _analyzer, _analyzerOptions, _cancellationToken); } return; diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index 19d913bc9aa..897a1161edf 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -31,9 +31,9 @@ internal partial class AnalyzerExecutor internal const string AnalyzerExceptionDiagnosticId = "AD0001"; internal const string AnalyzerDriverExceptionDiagnosticId = "AD0002"; - private readonly Action? _addNonCategorizedDiagnostic; - private readonly Action? _addCategorizedLocalDiagnostic; - private readonly Action? _addCategorizedNonLocalDiagnostic; + private readonly Action? _addNonCategorizedDiagnostic; + private readonly Action? _addCategorizedLocalDiagnostic; + private readonly Action? _addCategorizedNonLocalDiagnostic; private readonly Action? _addSuppression; private readonly Func? _analyzerExceptionFilter; private readonly AnalyzerManager _analyzerManager; @@ -51,6 +51,17 @@ internal partial class AnalyzerExecutor private readonly ConcurrentDictionary>? _analyzerExecutionTimeMap; private readonly CompilationAnalysisValueProviderFactory _compilationAnalysisValueProviderFactory; + /// + /// Cache of analyzer to analyzer specific options. If there are no specific + /// options, and the shared options should be used for all analyzers. This is the common case, which + /// means we don't pay for the extra indirection of a dictionary lookup normally. + /// + /// Note: this map is generated + /// at construction time, and is unchanging after that point. So it can be safely read from multiple + /// threads without need for locks. + /// + private readonly Dictionary? _analyzerToCachedOptions; + private Func? _lazyGetControlFlowGraph; private ConcurrentDictionary? _lazyControlFlowGraphMap; @@ -79,6 +90,10 @@ private bool IsAnalyzerSuppressedForTree(DiagnosticAnalyzer analyzer, SyntaxTree /// /// Delegate to determine if the given analyzer is compiler analyzer. /// We need to special case the compiler analyzer at few places for performance reasons. + /// Analyzers to query for custom options if is provided. + /// Optional callback to allow individual configuration options + /// on a per analyzer basis. /// Analyzer manager to fetch supported diagnostics. /// /// Delegate to fetch the gate object to guard all callbacks into the analyzer. @@ -98,10 +113,12 @@ private bool IsAnalyzerSuppressedForTree(DiagnosticAnalyzer analyzer, SyntaxTree public static AnalyzerExecutor Create( Compilation compilation, AnalyzerOptions analyzerOptions, - Action? addNonCategorizedDiagnostic, + Action? addNonCategorizedDiagnostic, Action onAnalyzerException, Func? analyzerExceptionFilter, Func isCompilerAnalyzer, + ImmutableArray diagnosticAnalyzers, + Func? getAnalyzerConfigOptionsProvider, AnalyzerManager analyzerManager, Func shouldSkipAnalysisOnGeneratedCode, Func shouldSuppressGeneratedCodeDiagnostic, @@ -111,8 +128,8 @@ public static AnalyzerExecutor Create( Func getSemanticModel, SeverityFilter severityFilter, bool logExecutionTime = false, - Action? addCategorizedLocalDiagnostic = null, - Action? addCategorizedNonLocalDiagnostic = null, + Action? addCategorizedLocalDiagnostic = null, + Action? addCategorizedNonLocalDiagnostic = null, Action? addSuppression = null) { // We can either report categorized (local/non-local) diagnostics or non-categorized diagnostics. @@ -122,7 +139,7 @@ public static AnalyzerExecutor Create( var analyzerExecutionTimeMap = logExecutionTime ? new ConcurrentDictionary>() : null; return new AnalyzerExecutor(compilation, analyzerOptions, addNonCategorizedDiagnostic, onAnalyzerException, analyzerExceptionFilter, - isCompilerAnalyzer, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation, + isCompilerAnalyzer, diagnosticAnalyzers, getAnalyzerConfigOptionsProvider, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation, isAnalyzerSuppressedForTree, getAnalyzerGate, getSemanticModel, severityFilter, analyzerExecutionTimeMap, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, addSuppression); } @@ -130,10 +147,12 @@ public static AnalyzerExecutor Create( private AnalyzerExecutor( Compilation compilation, AnalyzerOptions analyzerOptions, - Action? addNonCategorizedDiagnosticOpt, + Action? addNonCategorizedDiagnosticOpt, Action onAnalyzerException, Func? analyzerExceptionFilter, Func isCompilerAnalyzer, + ImmutableArray diagnosticAnalyzers, + Func? getAnalyzerConfigOptionsProvider, AnalyzerManager analyzerManager, Func shouldSkipAnalysisOnGeneratedCode, Func shouldSuppressGeneratedCodeDiagnostic, @@ -143,8 +162,8 @@ private AnalyzerExecutor( Func getSemanticModel, SeverityFilter severityFilter, ConcurrentDictionary>? analyzerExecutionTimeMap, - Action? addCategorizedLocalDiagnostic, - Action? addCategorizedNonLocalDiagnostic, + Action? addCategorizedLocalDiagnostic, + Action? addCategorizedNonLocalDiagnostic, Action? addSuppression) { Compilation = compilation; @@ -167,6 +186,36 @@ private AnalyzerExecutor( _addSuppression = addSuppression; _compilationAnalysisValueProviderFactory = new CompilationAnalysisValueProviderFactory(); + + if (getAnalyzerConfigOptionsProvider != null) + { + var hasDifferentOptions = false; + + var map = new Dictionary( + capacity: diagnosticAnalyzers.Length, ReferenceEqualityComparer.Instance); + + // Deduping map for the distinct AnalyzerConfigOptionsProvider we get back from getAnalyzerConfigOptionsProvider. + // The common case in VS host is that there is generally only 1-2 of these providers. For example, a provider + // that looks in editorconfig+vsoptions, and a provider that only looks in editorconfig. We only want to make + // a corresponding number of AnalyzerOptions instances for each unique provider we see. + var optionsProviderToOptions = new Dictionary(ReferenceEqualityComparer.Instance); + + foreach (var analyzer in diagnosticAnalyzers) + { + var specificOptionsProvider = getAnalyzerConfigOptionsProvider(analyzer); + var specificOptions = optionsProviderToOptions.GetOrAdd( + specificOptionsProvider, () => analyzerOptions.WithAnalyzerConfigOptionsProvider(specificOptionsProvider)); + map[analyzer] = specificOptions; + + if (specificOptions != analyzerOptions) + hasDifferentOptions = true; + } + + // Only if there is at least one analyzer with specific options, we need to maintain the map. + // Otherwise, we can just toss it and use the shared options for all analyzers. + if (hasDifferentOptions) + _analyzerToCachedOptions = map; + } } internal Compilation Compilation { get; } @@ -218,13 +267,17 @@ public void ExecuteInitializeMethod(HostSessionStartAnalysisScope sessionScope, public void ExecuteCompilationStartActions(ImmutableArray actions, HostCompilationStartAnalysisScope compilationScope, CancellationToken cancellationToken) { // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we - // can use the same instance across all actions. + // can use the same instance across all actions as long as the same options are picked for each analyzer. var context = new AnalyzerCompilationStartAnalysisContext(compilationScope, Compilation, AnalyzerOptions, _compilationAnalysisValueProviderFactory, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation); foreach (var startAction in actions) { + // See if we need to use an analyzer specific options instance. + context = WithAnalyzerSpecificOptions( + context, startAction.Analyzer, static (context, options) => context.WithOptions(options)); + ExecuteAndCatchIfThrows( startAction.Analyzer, static data => data.startAction.Action(data.context), @@ -234,6 +287,24 @@ public void ExecuteCompilationStartActions(ImmutableArray( + TAnalysisContext context, + DiagnosticAnalyzer analyzer, + Func withOptions) + { + // No specific options factory. Can use the shared context. + if (_analyzerToCachedOptions is null) + return context; + + return withOptions(context, GetAnalyzerSpecificOptions(analyzer)); + } + + /// + /// Given an analyzer, returns any specific options for it, or the shared options if none. + /// + private AnalyzerOptions GetAnalyzerSpecificOptions(DiagnosticAnalyzer analyzer) + => _analyzerToCachedOptions?[analyzer] ?? AnalyzerOptions; + /// /// Executes the symbol start actions. /// @@ -258,7 +329,7 @@ public void ExecuteSymbolStartActions( } // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we - // can use the same instance across all actions. + // can use the same instance across all actions (as long as the options stay the same per analyzer). var context = new AnalyzerSymbolStartAnalysisContext(symbolScope, symbol, Compilation, AnalyzerOptions, isGeneratedCodeSymbol, filterTree, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, symbol); @@ -267,6 +338,10 @@ public void ExecuteSymbolStartActions( { Debug.Assert(startAction.Analyzer == symbolScope.Analyzer); + // See if we need to use an analyzer specific options instance. + context = WithAnalyzerSpecificOptions( + context, startAction.Analyzer, static (context, options) => context.WithOptions(options)); + ExecuteAndCatchIfThrows( startAction.Analyzer, static data => data.startAction.Action(data.context), @@ -300,7 +375,8 @@ public void ExecuteSuppressionAction(DiagnosticSuppressor suppressor, ImmutableA supportedSuppressions, out Func isSupportedSuppression); - var context = new SuppressionAnalysisContext(Compilation, AnalyzerOptions, + var options = GetAnalyzerSpecificOptions(suppressor); + var context = new SuppressionAnalysisContext(Compilation, options, reportedDiagnostics, _addSuppression, isSupportedSuppression, _getSemanticModel, cancellationToken); ExecuteAndCatchIfThrows( @@ -326,7 +402,8 @@ public void ExecuteCompilationActions( { Debug.Assert(compilationEvent is CompilationStartedEvent || compilationEvent is CompilationCompletedEvent); - var addDiagnostic = GetAddCompilationDiagnostic(analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var addDiagnostic = GetAddCompilationDiagnostic(analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -336,7 +413,7 @@ public void ExecuteCompilationActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new CompilationAnalysisContext( - Compilation, AnalyzerOptions, addDiagnostic, + Compilation, analyzerOptions, addDiagnostic, isSupportedDiagnostic, _compilationAnalysisValueProviderFactory, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation); @@ -380,7 +457,9 @@ public void ExecuteSymbolActions( } var symbol = symbolDeclaredEvent.Symbol; - var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var addDiagnostic = GetAddDiagnostic( + symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, analyzerOptions, getTopMostNodeForAnalysis, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -390,7 +469,7 @@ public void ExecuteSymbolActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new SymbolAnalysisContext( - symbol, Compilation, AnalyzerOptions, addDiagnostic, + symbol, Compilation, analyzerOptions, addDiagnostic, isSupportedDiagnostic, isGeneratedCodeSymbol, filterTree, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, symbol); @@ -490,7 +569,8 @@ private void ExecuteSymbolEndActionsCore( Debug.Assert(!filterSpan.HasValue || filterTree != null); var symbol = symbolDeclaredEvent.Symbol; - var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, analyzerOptions, getTopMostNodeForAnalysis, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -499,7 +579,8 @@ private void ExecuteSymbolEndActionsCore( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. - var context = new SymbolAnalysisContext(symbol, Compilation, AnalyzerOptions, addDiagnostic, + var context = new SymbolAnalysisContext( + symbol, Compilation, analyzerOptions, addDiagnostic, isSupportedDiagnostic, isGeneratedCode, filterTree, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, symbol); @@ -539,7 +620,8 @@ public void ExecuteSemanticModelActions( return; } - var diagReporter = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -549,7 +631,7 @@ public void ExecuteSemanticModelActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new SemanticModelAnalysisContext( - semanticModel, AnalyzerOptions, diagReporter.AddDiagnosticAction, + semanticModel, analyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken); var contextInfo = new AnalysisContextInfo(semanticModel); @@ -592,7 +674,8 @@ public void ExecuteSyntaxTreeActions( return; } - var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -602,7 +685,7 @@ public void ExecuteSyntaxTreeActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new SyntaxTreeAnalysisContext( - tree, AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, + tree, analyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, Compilation, filterSpan, isGeneratedCode, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, file); @@ -637,7 +720,8 @@ public void ExecuteAdditionalFileActions( Debug.Assert(file.AdditionalFile != null); var additionalFile = file.AdditionalFile; - var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSyntaxDiagnostic(file, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -647,7 +731,7 @@ public void ExecuteAdditionalFileActions( // This context doesn't build up any state as we pass it to the Action method of the analyzer. As such, we // can use the same instance across all actions. var context = new AdditionalFileAnalysisContext( - additionalFile, AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, + additionalFile, analyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, Compilation, filterSpan, cancellationToken); var contextInfo = new AnalysisContextInfo(Compilation, file); @@ -677,7 +761,8 @@ private void ExecuteSyntaxNodeAction( Debug.Assert(!IsAnalyzerSuppressedForTree(syntaxNodeAction.Analyzer, node.SyntaxTree, cancellationToken)); var syntaxNodeContext = new SyntaxNodeAnalysisContext( - node, executionData.DeclaredSymbol, executionData.SemanticModel, AnalyzerOptions, addDiagnostic, + node, executionData.DeclaredSymbol, executionData.SemanticModel, + GetAnalyzerSpecificOptions(syntaxNodeAction.Analyzer), addDiagnostic, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); ExecuteAndCatchIfThrows( @@ -701,7 +786,7 @@ private void ExecuteOperationAction( var operationContext = new OperationAnalysisContext( operation, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, - AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, + GetAnalyzerSpecificOptions(operationAction.Analyzer), addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); ExecuteAndCatchIfThrows( @@ -714,12 +799,14 @@ private void ExecuteOperationAction( private readonly struct ExecutionData( DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, ISymbol declaredSymbol, SemanticModel semanticModel, TextSpan? filterSpan, bool isGeneratedCode) { public readonly DiagnosticAnalyzer Analyzer = analyzer; + public readonly AnalyzerOptions AnalyzerOptions = analyzerOptions; public readonly ISymbol DeclaredSymbol = declaredSymbol; public readonly SemanticModel SemanticModel = semanticModel; public readonly TextSpan? FilterSpan = filterSpan; @@ -748,12 +835,13 @@ public void ExecuteCodeBlockActions( // The actions we discover in 'addActions' and then execute in 'executeActions'. var ephemeralActions = ArrayBuilder>.GetInstance(); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); ExecuteBlockActionsCore( startActions, actions, endActions, declaredNode, - new ExecutionData(analyzer, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), addActions: static (startAction, endActions, executionData, args, cancellationToken) => { var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; @@ -761,7 +849,7 @@ public void ExecuteCodeBlockActions( var scope = new HostCodeBlockStartAnalysisScope(startAction.Analyzer); var startContext = new AnalyzerCodeBlockStartAnalysisContext( scope, declaredNode, executionData.DeclaredSymbol, executionData.SemanticModel, - @this.AnalyzerOptions, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); + executionData.AnalyzerOptions, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); // Catch Exception from the start action. @this.ExecuteAndCatchIfThrows( @@ -812,7 +900,7 @@ public void ExecuteCodeBlockActions( var (@this, startActions, executableCodeBlocks, declaredNode, getKind, ephemeralActions) = args; var context = new CodeBlockAnalysisContext(declaredNode, executionData.DeclaredSymbol, executionData.SemanticModel, - @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); + executionData.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); foreach (var blockAction in blockActions) { @@ -850,18 +938,20 @@ public void ExecuteOperationBlockActions( // The actions we discover in 'addActions' and then execute in 'executeActions'. var ephemeralActions = ArrayBuilder.GetInstance(); + + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); ExecuteBlockActionsCore( startActions, actions, endActions, declaredNode, - new ExecutionData(analyzer, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, semanticModel, filterSpan, isGeneratedCode), addActions: static (startAction, endActions, executionData, args, cancellationToken) => { var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; var scope = new HostOperationBlockStartAnalysisScope(startAction.Analyzer); var startContext = new AnalyzerOperationBlockStartAnalysisContext( - scope, operationBlocks, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, @this.AnalyzerOptions, + scope, operationBlocks, executionData.DeclaredSymbol, executionData.SemanticModel.Compilation, executionData.AnalyzerOptions, @this.GetControlFlowGraph, declaredNode.SyntaxTree, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); // Catch Exception from the start action. @@ -892,7 +982,7 @@ public void ExecuteOperationBlockActions( var (@this, startActions, declaredNode, operationBlocks, operations, ephemeralActions) = args; var context = new OperationBlockAnalysisContext(operationBlocks, executionData.DeclaredSymbol, @this.Compilation, - @this.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, + executionData.AnalyzerOptions, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, @this.GetControlFlowGraph, declaredNode.SyntaxTree, executionData.FilterSpan, executionData.IsGeneratedCode, cancellationToken); foreach (var blockAction in blockActions) @@ -948,7 +1038,8 @@ private void ExecuteBlockActionsCore( blockEndActions.AddAll(endActions); var diagReporter = GetAddSemanticDiagnostic( - executionData.SemanticModel.SyntaxTree, declaredNode.FullSpan, executionData.Analyzer, cancellationToken); + executionData.SemanticModel.SyntaxTree, declaredNode.FullSpan, + executionData.Analyzer, executionData.AnalyzerOptions, cancellationToken); // Include the stateful actions. foreach (var startAction in startActions) @@ -1012,7 +1103,9 @@ public void ExecuteSyntaxNodeActions( return; } - var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingTopmostNodeForAnalysis, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSemanticDiagnostic( + model.SyntaxTree, spanForContainingTopmostNodeForAnalysis, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -1021,7 +1114,7 @@ public void ExecuteSyntaxNodeActions( ExecuteSyntaxNodeActions( nodesToAnalyze, nodeActionsByKind, - new ExecutionData(analyzer, declaredSymbol, model, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, model, filterSpan, isGeneratedCode), getKind, diagReporter, isSupportedDiagnostic, hasCodeBlockStartOrSymbolStartActions, cancellationToken); diagReporter.Free(); @@ -1111,7 +1204,9 @@ public void ExecuteOperationActions( return; } - var diagReporter = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingOperationBlock, analyzer, cancellationToken); + var analyzerOptions = this.GetAnalyzerSpecificOptions(analyzer); + var diagReporter = GetAddSemanticDiagnostic( + model.SyntaxTree, spanForContainingOperationBlock, analyzer, analyzerOptions, cancellationToken); using var _ = PooledDelegates.GetPooledFunction( static (d, ct, arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), @@ -1120,7 +1215,7 @@ public void ExecuteOperationActions( ExecuteOperationActions( operationsToAnalyze, operationActionsByKind, - new ExecutionData(analyzer, declaredSymbol, model, filterSpan, isGeneratedCode), + new ExecutionData(analyzer, analyzerOptions, declaredSymbol, model, filterSpan, isGeneratedCode), diagReporter, isSupportedDiagnostic, hasOperationBlockStartOrSymbolStartActions, cancellationToken); diagReporter.Free(); @@ -1412,10 +1507,26 @@ private bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagn return _analyzerManager.IsSupportedDiagnostic(analyzer, diagnostic, _isCompilerAnalyzer, this, cancellationToken); } - private Action GetAddDiagnostic(ISymbol contextSymbol, ImmutableArray cachedDeclaringReferences, DiagnosticAnalyzer analyzer, Func getTopMostNodeForAnalysis, CancellationToken cancellationToken) + private Action GetAddDiagnostic( + ISymbol contextSymbol, + ImmutableArray cachedDeclaringReferences, + DiagnosticAnalyzer analyzer, + AnalyzerOptions options, + Func getTopMostNodeForAnalysis, + CancellationToken cancellationToken) { - return GetAddDiagnostic(contextSymbol, cachedDeclaringReferences, Compilation, analyzer, _addNonCategorizedDiagnostic, - _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, getTopMostNodeForAnalysis, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); + return GetAddDiagnostic( + contextSymbol, + cachedDeclaringReferences, + Compilation, + analyzer, + options, + _addNonCategorizedDiagnostic, + _addCategorizedLocalDiagnostic, + _addCategorizedNonLocalDiagnostic, + getTopMostNodeForAnalysis, + _shouldSuppressGeneratedCodeDiagnostic, + cancellationToken); } private static Action GetAddDiagnostic( @@ -1423,9 +1534,10 @@ private static Action GetAddDiagnostic( ImmutableArray cachedDeclaringReferences, Compilation compilation, DiagnosticAnalyzer analyzer, - Action? addNonCategorizedDiagnostic, - Action? addCategorizedLocalDiagnostic, - Action? addCategorizedNonLocalDiagnostic, + AnalyzerOptions options, + Action? addNonCategorizedDiagnostic, + Action? addCategorizedLocalDiagnostic, + Action? addCategorizedNonLocalDiagnostic, Func getTopMostNodeForAnalysis, Func shouldSuppressGeneratedCodeDiagnostic, CancellationToken cancellationToken) @@ -1440,7 +1552,7 @@ private static Action GetAddDiagnostic( if (addCategorizedLocalDiagnostic == null) { Debug.Assert(addNonCategorizedDiagnostic != null); - addNonCategorizedDiagnostic(diagnostic, cancellationToken); + addNonCategorizedDiagnostic(diagnostic, options, cancellationToken); return; } @@ -1456,18 +1568,21 @@ private static Action GetAddDiagnostic( var syntax = getTopMostNodeForAnalysis(contextSymbol, syntaxRef, compilation, cancellationToken); if (diagnostic.Location.SourceSpan.IntersectsWith(syntax.FullSpan)) { - addCategorizedLocalDiagnostic(diagnostic, analyzer, false, cancellationToken); + addCategorizedLocalDiagnostic(diagnostic, analyzer, options, false, cancellationToken); return; } } } } - addCategorizedNonLocalDiagnostic(diagnostic, analyzer, cancellationToken); + addCategorizedNonLocalDiagnostic(diagnostic, analyzer, options, cancellationToken); }; } - private Action GetAddCompilationDiagnostic(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private Action GetAddCompilationDiagnostic( + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { return diagnostic => { @@ -1479,31 +1594,47 @@ private Action GetAddCompilationDiagnostic(DiagnosticAnalyzer analyz if (_addCategorizedNonLocalDiagnostic == null) { Debug.Assert(_addNonCategorizedDiagnostic != null); - _addNonCategorizedDiagnostic(diagnostic, cancellationToken); + _addNonCategorizedDiagnostic(diagnostic, analyzerOptions, cancellationToken); return; } - _addCategorizedNonLocalDiagnostic(diagnostic, analyzer, cancellationToken); + _addCategorizedNonLocalDiagnostic(diagnostic, analyzer, analyzerOptions, cancellationToken); }; } - private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic( + SyntaxTree tree, + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { - return AnalyzerDiagnosticReporter.GetInstance(new SourceOrAdditionalFile(tree), span: null, Compilation, analyzer, isSyntaxDiagnostic: false, + return AnalyzerDiagnosticReporter.GetInstance( + new SourceOrAdditionalFile(tree), span: null, Compilation, analyzer, analyzerOptions, isSyntaxDiagnostic: false, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); } - private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic(SyntaxTree tree, TextSpan? span, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic( + SyntaxTree tree, + TextSpan? span, + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { - return AnalyzerDiagnosticReporter.GetInstance(new SourceOrAdditionalFile(tree), span, Compilation, analyzer, isSyntaxDiagnostic: false, + return AnalyzerDiagnosticReporter.GetInstance( + new SourceOrAdditionalFile(tree), span, Compilation, analyzer, analyzerOptions, isSyntaxDiagnostic: false, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); } - private AnalyzerDiagnosticReporter GetAddSyntaxDiagnostic(SourceOrAdditionalFile file, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) + private AnalyzerDiagnosticReporter GetAddSyntaxDiagnostic( + SourceOrAdditionalFile file, + DiagnosticAnalyzer analyzer, + AnalyzerOptions analyzerOptions, + CancellationToken cancellationToken) { - return AnalyzerDiagnosticReporter.GetInstance(file, span: null, Compilation, analyzer, isSyntaxDiagnostic: true, + return AnalyzerDiagnosticReporter.GetInstance( + file, span: null, Compilation, analyzer, analyzerOptions, isSyntaxDiagnostic: true, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken); } diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs index 0b34cc33b77..a79219b0a37 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptions.cs @@ -63,6 +63,11 @@ public AnalyzerOptions WithAdditionalFiles(ImmutableArray additi return new AnalyzerOptions(additionalFiles); } + internal AnalyzerOptions WithAnalyzerConfigOptionsProvider(AnalyzerConfigOptionsProvider optionsProvider) + => this.AnalyzerConfigOptionsProvider == optionsProvider + ? this + : new(this.AdditionalFiles, optionsProvider); + public override bool Equals(object? obj) { if (ReferenceEquals(this, obj)) diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs index e7b8ac7b8f7..62b28f20ac7 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerOptionsExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.InternalUtilities; @@ -19,6 +20,9 @@ internal static class AnalyzerOptionsExtensions private static string GetCategoryBasedDotnetAnalyzerDiagnosticSeverityKey(string category) => s_categoryToSeverityKeyMap.GetOrAdd(category, category, static category => $"{DotnetAnalyzerDiagnosticPrefix}.{CategoryPrefix}-{category}.{SeveritySuffix}"); + public static ImmutableArray GetAdditionalFiles(this AnalyzerOptions? analyzerOptions) + => analyzerOptions?.AdditionalFiles ?? []; + /// /// Tries to get configured severity for the given /// for the given from bulk configuration analyzer config options, i.e. diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs index 67c47debe03..c3a5afae49e 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzers.cs @@ -104,7 +104,7 @@ public CompilationWithAnalyzers(Compilation compilation, ImmutableArray().ToImmutableArrayOrEmpty(); _analysisOptions = analysisOptions; - _analysisResultBuilder = new AnalysisResultBuilder(analysisOptions.LogAnalyzerExecutionTime, analyzers, _analysisOptions.Options?.AdditionalFiles ?? ImmutableArray.Empty); + _analysisResultBuilder = new AnalysisResultBuilder(analysisOptions.LogAnalyzerExecutionTime, analyzers, _analysisOptions.Options.GetAdditionalFiles()); _compilationAnalysisScope = AnalysisScope.Create(_compilation, _analyzers, this); } @@ -224,7 +224,7 @@ private void VerifyAdditionalFile(AdditionalText file) #endregion - private ImmutableArray AdditionalFiles => _analysisOptions.Options?.AdditionalFiles ?? ImmutableArray.Empty; + private ImmutableArray AdditionalFiles => _analysisOptions.Options.GetAdditionalFiles(); /// /// Returns diagnostics produced by all . diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs index 015cf46f382..33cf088af40 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/CompilationWithAnalyzersOptions.cs @@ -48,6 +48,13 @@ public sealed class CompilationWithAnalyzersOptions /// public bool ReportSuppressedDiagnostics => _reportSuppressedDiagnostics; + /// + /// Callback to allow individual analyzers to have their own + /// distinct from the shared instance provided in . If then will be used for all analyzers. + /// + internal readonly Func? GetAnalyzerConfigOptionsProvider; + /// /// Creates a new . /// @@ -98,6 +105,23 @@ public CompilationWithAnalyzersOptions( bool logAnalyzerExecutionTime, bool reportSuppressedDiagnostics, Func? analyzerExceptionFilter) + : this(options, onAnalyzerException, concurrentAnalysis, logAnalyzerExecutionTime, reportSuppressedDiagnostics, analyzerExceptionFilter, getAnalyzerConfigOptionsProvider: null) + { + } + + /// + /// Callback to allow individual analyzers to have their own distinct from the shared instance provided in . If then will be used for all + /// analyzers. + public CompilationWithAnalyzersOptions( + AnalyzerOptions? options, + Action? onAnalyzerException, + bool concurrentAnalysis, + bool logAnalyzerExecutionTime, + bool reportSuppressedDiagnostics, + Func? analyzerExceptionFilter, + Func? getAnalyzerConfigOptionsProvider) { _options = options; _onAnalyzerException = onAnalyzerException; @@ -105,6 +129,7 @@ public CompilationWithAnalyzersOptions( _concurrentAnalysis = concurrentAnalysis; _logAnalyzerExecutionTime = logAnalyzerExecutionTime; _reportSuppressedDiagnostics = reportSuppressedDiagnostics; + this.GetAnalyzerConfigOptionsProvider = getAnalyzerConfigOptionsProvider; } } } diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs index db530e231f9..9fa5b256030 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs @@ -215,6 +215,11 @@ internal override bool TryGetValueCore(TKey key, AnalysisValueProv var compilationAnalysisValueProvider = _compilationAnalysisValueProviderFactory.GetValueProvider(valueProvider); return compilationAnalysisValueProvider.TryGetValue(key, out value); } + + public AnalyzerCompilationStartAnalysisContext WithOptions(AnalyzerOptions options) + => this.Options == options + ? this + : new(_scope, this.Compilation, options, _compilationAnalysisValueProviderFactory, this.CancellationToken); } /// @@ -279,6 +284,11 @@ public override void RegisterOperationAction(Action ac DiagnosticAnalysisContextHelpers.VerifyArguments(action, operationKinds); _scope.RegisterOperationAction(action, operationKinds); } + + public AnalyzerSymbolStartAnalysisContext WithOptions(AnalyzerOptions analyzerOptions) + => this.Options == analyzerOptions + ? this + : new(_scope, this.Symbol, this.Compilation, analyzerOptions, this.IsGeneratedCode, this.FilterTree, this.FilterSpan, this.CancellationToken); } /// diff --git a/src/roslyn/src/Compilers/Core/Portable/DocumentationCommentId.cs b/src/roslyn/src/Compilers/Core/Portable/DocumentationCommentId.cs index 607e111ae06..4f5d16404d9 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DocumentationCommentId.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DocumentationCommentId.cs @@ -382,6 +382,12 @@ public override void VisitNamedType(INamedTypeSymbol symbol) { _builder.Append("T:"); _generator.Visit(symbol); + + if (symbol.IsExtension) + { + _builder.Append('.'); + _builder.Append(symbol.ExtensionMarkerName); + } } private sealed class DeclarationGenerator : SymbolVisitor @@ -525,7 +531,7 @@ public override bool VisitNamedType(INamedTypeSymbol symbol) _builder.Append('.'); } - _builder.Append(EncodeName(symbol.Name)); + _builder.Append(EncodeName(symbol.IsExtension ? symbol.ExtensionGroupingName : symbol.Name)); if (symbol.TypeParameters.Length > 0) { @@ -561,7 +567,14 @@ private void BuildDottedName(ISymbol symbol) _builder.Append('.'); } - _builder.Append(EncodeName(symbol.Name)); + if (symbol is INamedTypeSymbol { IsExtension: true } extension) + { + _builder.Append(EncodeName(extension.ExtensionGroupingName)); + } + else + { + _builder.Append(EncodeName(symbol.Name)); + } } public override bool VisitAlias(IAliasSymbol symbol) @@ -583,7 +596,19 @@ public override bool VisitNamespace(INamespaceSymbol symbol) public override bool VisitNamedType(INamedTypeSymbol symbol) { this.BuildDottedName(symbol); + AppendArityOrTypeArguments(symbol); + + if (symbol.IsExtension) + { + _builder.Append('.'); + _builder.Append(symbol.ExtensionMarkerName); + } + + return true; + } + private void AppendArityOrTypeArguments(INamedTypeSymbol symbol) + { if (symbol.IsGenericType) { if (symbol.OriginalDefinition == symbol) @@ -608,8 +633,6 @@ public override bool VisitNamedType(INamedTypeSymbol symbol) _builder.Append('}'); } } - - return true; } public override bool VisitDynamicType(IDynamicTypeSymbol symbol) @@ -802,7 +825,7 @@ private static void ParseDeclaredId(string id, ref int index, Compilation compil if (arity > 0) { // only types have arity - GetMatchingTypes(containers, name, arity, results); + GetMatchingTypes(containers, name, arity, isTerminal: false, results); } else if (kind == SymbolKind.Namespace) { @@ -839,7 +862,7 @@ private static void ParseDeclaredId(string id, ref int index, Compilation compil GetMatchingMethods(id, ref index, containers, name, arity, compilation, results); break; case SymbolKind.NamedType: - GetMatchingTypes(containers, name, arity, results); + GetMatchingTypes(containers, name, arity, isTerminal: true, results); break; case SymbolKind.Property: GetMatchingProperties(id, ref index, containers, name, compilation, results); @@ -1042,7 +1065,7 @@ private static void ParseNamedTypeSymbol(string id, ref int index, Compilation c if (arity != 0 || PeekNextChar(id, index) != '.') { - GetMatchingTypes(containers, name, arity, results); + GetMatchingTypes(containers, name, arity, isTerminal: false, results); if (arity != 0 && typeArguments != null && typeArguments.Count != 0) { @@ -1152,16 +1175,29 @@ private static bool ParseTypeArguments(string id, ref int index, Compilation com return true; } - private static void GetMatchingTypes(List containers, string memberName, int arity, List results) + private static void GetMatchingTypes(List containers, string memberName, int arity, bool isTerminal, List results) { for (int i = 0, n = containers.Count; i < n; i++) { - GetMatchingTypes(containers[i], memberName, arity, results); + GetMatchingTypes(containers[i], memberName, arity, isTerminal: isTerminal, results); } } - private static void GetMatchingTypes(INamespaceOrTypeSymbol container, string memberName, int arity, List results) + /// Indicates that we're looking at the last segment in a dotted chain. + /// If we're in terminal position, we need to recognize the extension marker name so that + /// `ContainingType.ExtensionGroupingName.ExtensionMarkerName` can be matched to the extension type. + /// + private static void GetMatchingTypes(INamespaceOrTypeSymbol container, string memberName, int arity, bool isTerminal, List results) { + if (isTerminal + && container is INamedTypeSymbol { IsExtension: true } extension + && extension.ExtensionMarkerName == memberName + && arity == 0) + { + results.Add(extension); + return; + } + var members = container.GetMembers(memberName); foreach (var symbol in members) @@ -1175,6 +1211,8 @@ private static void GetMatchingTypes(INamespaceOrTypeSymbol container, string me } } } + + GetMatchingExtensions(container, memberName, arity, results); } private static void GetMatchingNamespaceOrTypes(List containers, string memberName, List results) @@ -1196,6 +1234,27 @@ private static void GetMatchingNamespaceOrTypes(INamespaceOrTypeSymbol container results.Add(symbol); } } + + GetMatchingExtensions(container, memberName, arity: 0, results); + } + + private static void GetMatchingExtensions(INamespaceOrTypeSymbol container, string memberName, int arity, List results) + { + if (container.IsNamespace) + { + return; + } + + ImmutableArray unnamedNamedTypes = container.GetTypeMembers(""); + foreach (var namedType in unnamedNamedTypes) + { + if (namedType.IsExtension + && namedType.Arity == arity + && namedType.ExtensionGroupingName == memberName) + { + results.Add(namedType); + } + } } private static void GetMatchingNamespaces(List containers, string memberName, List results) diff --git a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs index 559fc24c63a..84803050a94 100644 --- a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs +++ b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/PlatformInformation.cs @@ -57,5 +57,7 @@ public static bool IsUsingMonoRuntime } } } + + public static string ExeExtension => IsWindows ? ".exe" : string.Empty; } } diff --git a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs index c727e4c7da1..4e141e9d807 100644 --- a/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs +++ b/src/roslyn/src/Compilers/Core/Portable/InternalUtilities/StringExtensions.cs @@ -138,20 +138,20 @@ internal static bool TryGetWithoutAttributeSuffix( } internal static string? GetWithoutAttributeSuffix( - this string name, + this string? name, bool isCaseSensitive) { return TryGetWithoutAttributeSuffix(name, isCaseSensitive, out var result) ? result : null; } internal static bool TryGetWithoutAttributeSuffix( - this string name, + this string? name, bool isCaseSensitive, [NotNullWhen(returnValue: true)] out string? result) { if (name.HasAttributeSuffix(isCaseSensitive)) { - result = name.Substring(0, name.Length - AttributeSuffix.Length); + result = name[..^AttributeSuffix.Length]; return true; } @@ -159,8 +159,11 @@ internal static bool TryGetWithoutAttributeSuffix( return false; } - internal static bool HasAttributeSuffix(this string name, bool isCaseSensitive) + internal static bool HasAttributeSuffix([NotNullWhen(true)] this string? name, bool isCaseSensitive) { + if (name is null) + return false; + var comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; return name.Length > AttributeSuffix.Length && name.EndsWith(AttributeSuffix, comparison); } diff --git a/src/roslyn/src/Compilers/Core/Portable/MethodImplExtensions.cs b/src/roslyn/src/Compilers/Core/Portable/MethodImplExtensions.cs new file mode 100644 index 00000000000..5d4b62f0018 --- /dev/null +++ b/src/roslyn/src/Compilers/Core/Portable/MethodImplExtensions.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Microsoft.CodeAnalysis; + +internal static class MethodImplAttributeExtensions +{ + extension(MethodImplAttributes) + { + // https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible + public static MethodImplAttributes Async => (MethodImplAttributes)0x2000; + } +} + +internal static class MethodImplOptionsExtensions +{ + extension(MethodImplOptions) + { + // https://github.com/dotnet/roslyn/issues/79792: Use the real value when possible + public static MethodImplOptions Async => (MethodImplOptions)0x2000; + } +} diff --git a/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 3dd2e036998..fb0ee6a5ae6 100644 --- a/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/roslyn/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -8,11 +8,17 @@ Microsoft.CodeAnalysis.CommandLineResource.IsPublic.get -> bool Microsoft.CodeAnalysis.CommandLineResource.LinkedResourceFileName.get -> string? Microsoft.CodeAnalysis.CommandLineResource.ResourceName.get -> string! Microsoft.CodeAnalysis.Compilation.EmitDifference(Microsoft.CodeAnalysis.Emit.EmitBaseline! baseline, System.Collections.Generic.IEnumerable! edits, System.Func! isAddedSymbol, System.IO.Stream! metadataStream, System.IO.Stream! ilStream, System.IO.Stream! pdbStream, Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitDifferenceResult! +Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.CompilationWithAnalyzersOptions(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions? options, System.Action? onAnalyzerException, bool concurrentAnalysis, bool logAnalyzerExecutionTime, bool reportSuppressedDiagnostics, System.Func? analyzerExceptionFilter, System.Func? getAnalyzerConfigOptionsProvider) -> void Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitDifferenceOptions() -> void Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitFieldRva.get -> bool Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.EmitFieldRva.init -> void +Microsoft.CodeAnalysis.IMethodSymbol.AssociatedExtensionImplementation.get -> Microsoft.CodeAnalysis.IMethodSymbol? Microsoft.CodeAnalysis.IMethodSymbol.IsIterator.get -> bool +Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionGroupingName.get -> string? +Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionMarkerName.get -> string? +Microsoft.CodeAnalysis.INamedTypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? +Microsoft.CodeAnalysis.INamedTypeSymbol.IsExtension.get -> bool static readonly Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions.Default -> Microsoft.CodeAnalysis.Emit.EmitDifferenceOptions Microsoft.CodeAnalysis.IEventSymbol.IsPartialDefinition.get -> bool Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index e292d1f3f5f..ed9e3b9b34e 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -493,5 +493,6 @@ static AttributeDescription() internal static readonly AttributeDescription CompilerLoweringPreserveAttribute = new AttributeDescription("System.Runtime.CompilerServices", "CompilerLoweringPreserveAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription ExtensionMarkerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "ExtensionMarkerAttribute", s_signatures_HasThis_Void_String_Only); internal static readonly AttributeDescription RuntimeAsyncMethodGenerationAttribute = new AttributeDescription("System.Runtime.CompilerServices", "RuntimeAsyncMethodGenerationAttribute", s_signatures_HasThis_Void_Boolean_Only); + internal static readonly AttributeDescription MetadataUpdateDeletedAttribute = new AttributeDescription("System.Runtime.CompilerServices", "MetadataUpdateDeletedAttribute", s_signatures_HasThis_Void_Only); } } diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs index b4f4afda3d9..b83974f744d 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs @@ -427,9 +427,7 @@ internal static void DecodeMethodImplAttribute /// Gets the top-level nullability of this field. /// + /// + /// The inferred nullability of a backing field symbol (for example, the symbol for a keyword) is not exposed by this API. + /// In that case, this API returns the nullable annotation of the associated property. + /// The inferred nullability is only exposed indirectly via APIs that give information about expressions, such as when called with a expression. + /// NullableAnnotation NullableAnnotation { get; } /// diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs index 4a46d267649..6791aabca03 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/IMethodSymbol.cs @@ -43,7 +43,7 @@ public interface IMethodSymbol : ISymbol /// /// Returns false for methods in extension() blocks. /// To check if a method is a "new" extension method (a member of an extension() block), - /// check on the method's . + /// check on the method's . /// bool IsExtensionMethod { get; } @@ -304,5 +304,25 @@ public interface IMethodSymbol : ISymbol /// Returns if this method is a source method implemented as an iterator (either sync or async) /// bool IsIterator { get; } + + // Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, add support for constructed symbols + /// + /// For a method/accessor/operator in an extension block, returns the corresponding implementation method if one exists. + /// Returns null otherwise. + /// + /// For example, considering: + /// + /// static class E + /// { + /// extension(int i) + /// { + /// public void M() { } + /// } + /// } + /// + /// When given the method symbol for E.extension(int i).M(), + /// it will return the corresponding static implementation method E.M(this int i). + /// + IMethodSymbol? AssociatedExtensionImplementation { get; } } } diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs index a5a2d685d6d..a3fc0637730 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; @@ -196,5 +197,30 @@ public interface INamedTypeSymbol : ITypeSymbol /// Otherwise, returns null. /// INamedTypeSymbol? NativeIntegerUnderlyingType { get; } + + /// + /// Is this a symbol for an extension declaration. + /// + [MemberNotNullWhen(true, nameof(ExtensionGroupingName), nameof(ExtensionMarkerName))] + new bool IsExtension { get; } + + /// + /// The extension parameter if this is an extension declaration ( is true). + /// Note: this may be null even if is true, in error cases. + /// + new IParameterSymbol? ExtensionParameter { get; } + + /// + /// For extensions, returns the synthesized identifier for the grouping type. + /// Returns null otherwise. + /// Note: the metadata name for generic grouping types includes an arity suffix, so differs from the ExtensionGroupingName property. + /// + string? ExtensionGroupingName { get; } + + /// + /// For extensions, returns the synthesized identifier for the marker type. + /// Returns null otherwise. + /// + string? ExtensionMarkerName { get; } } } diff --git a/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs b/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs index 176ba1cfe07..85bb9c9c39d 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs @@ -81,15 +81,10 @@ public interface ITypeSymbol : INamespaceOrTypeSymbol /// bool IsNativeIntegerType { get; } - /// - /// Is this a symbol for an extension declaration. - /// + [Obsolete($"This API will be removed in the future. Use {nameof(INamedTypeSymbol)}.{nameof(INamedTypeSymbol.IsExtension)} instead.")] bool IsExtension { get; } - /// - /// The extension parameter if this is an extension declaration ( is true). - /// Note: this may be null even if is true, in error cases. - /// + [Obsolete($"This API will be removed in the future. Use {nameof(INamedTypeSymbol)}.{nameof(INamedTypeSymbol.ExtensionParameter)} instead.")] IParameterSymbol? ExtensionParameter { get; } /// diff --git a/src/roslyn/src/Compilers/Core/Portable/Text/SourceTextStream.cs b/src/roslyn/src/Compilers/Core/Portable/Text/SourceTextStream.cs index 4d7de75c6a3..de4dc522cc1 100644 --- a/src/roslyn/src/Compilers/Core/Portable/Text/SourceTextStream.cs +++ b/src/roslyn/src/Compilers/Core/Portable/Text/SourceTextStream.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.IO; using System.Text; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Text { @@ -18,17 +19,20 @@ internal sealed class SourceTextStream : Stream private readonly Encoding _encoding; private readonly Encoder _encoder; + internal const int BufferSize = 2048; + private static readonly ObjectPool s_charArrayPool = new ObjectPool(() => new char[BufferSize], size: 8); + private readonly int _minimumTargetBufferCount; private int _position; private int _sourceOffset; - private readonly char[] _charBuffer; + private char[] _charBuffer; private int _bufferOffset; private int _bufferUnreadChars; private bool _preambleWritten; private static readonly Encoding s_utf8EncodingWithNoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false); - public SourceTextStream(SourceText source, int bufferSize = 2048, bool useDefaultEncodingIfNull = false) + public SourceTextStream(SourceText source, bool useDefaultEncodingIfNull = false) { Debug.Assert(source.Encoding != null || useDefaultEncodingIfNull); @@ -38,12 +42,23 @@ public SourceTextStream(SourceText source, int bufferSize = 2048, bool useDefaul _minimumTargetBufferCount = _encoding.GetMaxByteCount(charCount: 1); _sourceOffset = 0; _position = 0; - _charBuffer = new char[Math.Min(bufferSize, _source.Length)]; + _charBuffer = s_charArrayPool.Allocate(); _bufferOffset = 0; _bufferUnreadChars = 0; _preambleWritten = false; } + protected override void Dispose(bool disposing) + { + if (disposing && _charBuffer != null) + { + s_charArrayPool.Free(_charBuffer); + _charBuffer = null!; + } + + base.Dispose(disposing); + } + public override bool CanRead { get { return true; } diff --git a/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj b/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj index 12e7ee7325c..e0ff037f975 100644 --- a/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj +++ b/src/roslyn/src/Compilers/Core/SdkTaskTests/Microsoft.Build.Tasks.CodeAnalysis.Sdk.UnitTests.csproj @@ -58,5 +58,6 @@ + diff --git a/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs b/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs index 9c235ed8f8f..1c86e743c7d 100644 --- a/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.cs +++ b/src/roslyn/src/Compilers/Core/SdkTaskTests/SdkManagedToolTests.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.IO; -using Roslyn.Test.Utilities; -using Microsoft.CodeAnalysis.Test.Utilities; -using Xunit.Abstractions; using Xunit; +using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.BuildTasks.Sdk.UnitTests; @@ -25,7 +22,12 @@ public void PathToBuiltinTool() { var taskPath = Path.GetDirectoryName(typeof(ManagedCompiler).Assembly.Location)!; var task = new Csc(); - Assert.Equal(Path.Combine(taskPath, "..", "bincore", "csc.dll"), task.PathToBuiltInTool); + Assert.Contains(task.PathToBuiltInTool, + new[] + { + Path.Combine(taskPath, "..", "bincore", "csc.dll"), + Path.Combine(taskPath, "..", "bincore", "csc.exe"), + }); } [Fact] diff --git a/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj b/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj index ab0107e8807..63820995a72 100644 --- a/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj +++ b/src/roslyn/src/Compilers/Server/VBCSCompiler/AnyCpu/VBCSCompiler.csproj @@ -4,8 +4,8 @@ Exe $(NetRoslynSourceBuild);net472 - false true + false diff --git a/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs b/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs index 4d111d88a99..d8f7a830b2a 100644 --- a/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs +++ b/src/roslyn/src/Compilers/Server/VBCSCompilerTests/CompilerServerApiTest.cs @@ -130,9 +130,8 @@ public async Task IncorrectServerHashReturnsIncorrectHashResponse() public void QuotePipeName_Desktop() { var serverInfo = BuildServerConnection.GetServerProcessInfo(@"q:\tools", "name with space"); - // Because q:\tools\VBCSCompiler.exe does not exist, the fallback 'dotnet exec VBCSCompiler.dll' is returned. Assert.EndsWith(@"\dotnet.exe", serverInfo.processFilePath); - Assert.Equal(@"exec ""q:\tools\VBCSCompiler.dll"" ""-pipename:name with space""", serverInfo.commandLineArguments); + AssertEx.Equal(@"exec ""q:\tools\VBCSCompiler.dll"" ""-pipename:name with space""", serverInfo.commandLineArguments); } [ConditionalFact(typeof(CoreClrOnly))] @@ -144,7 +143,7 @@ public void QuotePipeName_CoreClr() : "/tools"; var serverInfo = BuildServerConnection.GetServerProcessInfo(toolDir, "name with space"); var vbcsFilePath = Path.Combine(toolDir, "VBCSCompiler.dll"); - Assert.Equal($@"exec ""{vbcsFilePath}"" ""-pipename:name with space""", serverInfo.commandLineArguments); + AssertEx.Equal($@"exec ""{vbcsFilePath}"" ""-pipename:name with space""", serverInfo.commandLineArguments); } [Theory] diff --git a/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs b/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs index 732627d6096..c968fec249d 100644 --- a/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs +++ b/src/roslyn/src/Compilers/Shared/BuildServerConnection.cs @@ -432,11 +432,12 @@ internal static async Task MonitorDisconnectAsync( internal static (string processFilePath, string commandLineArguments) GetServerProcessInfo(string clientDir, string pipeName) { - var processFilePath = Path.Combine(clientDir, "VBCSCompiler.exe"); + var processFilePath = Path.Combine(clientDir, $"VBCSCompiler{PlatformInformation.ExeExtension}"); var commandLineArgs = $@"""-pipename:{pipeName}"""; + if (!File.Exists(processFilePath)) { - // This is a .NET Core deployment + // Fallback to not use the apphost if it is not present (can happen in compiler toolset scenarios for example). commandLineArgs = RuntimeHostInfo.GetDotNetExecCommandLine(Path.ChangeExtension(processFilePath, ".dll"), commandLineArgs); processFilePath = RuntimeHostInfo.GetDotNetPathOrDefault(); } @@ -462,81 +463,95 @@ internal static bool TryCreateServer(string clientDirectory, string pipeName, IC logger.Log("Attempting to create process '{0}' {1}", serverInfo.processFilePath, serverInfo.commandLineArguments); - if (PlatformInformation.IsWindows) + string? previousDotNetRoot = Environment.GetEnvironmentVariable(RuntimeHostInfo.DotNetRootEnvironmentName); + if (RuntimeHostInfo.GetToolDotNetRoot() is { } dotNetRoot) { - // As far as I can tell, there isn't a way to use the Process class to - // create a process with no stdin/stdout/stderr, so we use P/Invoke. - // This code was taken from MSBuild task starting code. - - STARTUPINFO startInfo = new STARTUPINFO(); - startInfo.cb = Marshal.SizeOf(startInfo); - startInfo.hStdError = InvalidIntPtr; - startInfo.hStdInput = InvalidIntPtr; - startInfo.hStdOutput = InvalidIntPtr; - startInfo.dwFlags = STARTF_USESTDHANDLES; - uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; - - PROCESS_INFORMATION processInfo; - - var builder = new StringBuilder($@"""{serverInfo.processFilePath}"" {serverInfo.commandLineArguments}"); - - bool success = CreateProcess( - lpApplicationName: null, - lpCommandLine: builder, - lpProcessAttributes: NullPtr, - lpThreadAttributes: NullPtr, - bInheritHandles: false, - dwCreationFlags: dwCreationFlags, - lpEnvironment: NullPtr, // Inherit environment - lpCurrentDirectory: clientDirectory, - lpStartupInfo: ref startInfo, - lpProcessInformation: out processInfo); - - if (success) - { - logger.Log("Successfully created process with process id {0}", processInfo.dwProcessId); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - processId = processInfo.dwProcessId; - } - else - { - logger.LogError("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); - } - return success; + logger.Log("Setting {0} to '{1}'", RuntimeHostInfo.DotNetRootEnvironmentName, dotNetRoot); + Environment.SetEnvironmentVariable(RuntimeHostInfo.DotNetRootEnvironmentName, dotNetRoot); } - else + + try { - try + if (PlatformInformation.IsWindows) { - var startInfo = new ProcessStartInfo() - { - FileName = serverInfo.processFilePath, - Arguments = serverInfo.commandLineArguments, - UseShellExecute = false, - WorkingDirectory = clientDirectory, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - if (Process.Start(startInfo) is { } process) + // As far as I can tell, there isn't a way to use the Process class to + // create a process with no stdin/stdout/stderr, so we use P/Invoke. + // This code was taken from MSBuild task starting code. + + STARTUPINFO startInfo = new STARTUPINFO(); + startInfo.cb = Marshal.SizeOf(startInfo); + startInfo.hStdError = InvalidIntPtr; + startInfo.hStdInput = InvalidIntPtr; + startInfo.hStdOutput = InvalidIntPtr; + startInfo.dwFlags = STARTF_USESTDHANDLES; + uint dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW; + + PROCESS_INFORMATION processInfo; + + var builder = new StringBuilder($@"""{serverInfo.processFilePath}"" {serverInfo.commandLineArguments}"); + + bool success = CreateProcess( + lpApplicationName: null, + lpCommandLine: builder, + lpProcessAttributes: NullPtr, + lpThreadAttributes: NullPtr, + bInheritHandles: false, + dwCreationFlags: dwCreationFlags, + lpEnvironment: NullPtr, // Inherit environment + lpCurrentDirectory: clientDirectory, + lpStartupInfo: ref startInfo, + lpProcessInformation: out processInfo); + + if (success) { - processId = process.Id; - logger.Log("Successfully created process with process id {0}", processId); - return true; + logger.Log("Successfully created process with process id {0}", processInfo.dwProcessId); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + processId = processInfo.dwProcessId; } else { - return false; + logger.LogError("Failed to create process. GetLastError={0}", Marshal.GetLastWin32Error()); } + return success; } - catch + else { - return false; + try + { + var startInfo = new ProcessStartInfo() + { + FileName = serverInfo.processFilePath, + Arguments = serverInfo.commandLineArguments, + UseShellExecute = false, + WorkingDirectory = clientDirectory, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + + if (Process.Start(startInfo) is { } process) + { + processId = process.Id; + logger.Log("Successfully created process with process id {0}", processId); + return true; + } + else + { + return false; + } + } + catch + { + return false; + } } } + finally + { + Environment.SetEnvironmentVariable(RuntimeHostInfo.DotNetRootEnvironmentName, previousDotNetRoot); + } } /// diff --git a/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs b/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs index 7ff9b16c7df..a77574e59cd 100644 --- a/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs +++ b/src/roslyn/src/Compilers/Shared/RuntimeHostInfo.cs @@ -5,10 +5,7 @@ #nullable enable using System; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; -using System.IO.Pipes; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -21,9 +18,6 @@ internal static class RuntimeHostInfo { internal static bool IsDesktopRuntime => !IsCoreClrRuntime; - internal static string GetDotNetExecCommandLine(string toolFilePath, string commandLineArguments) => - $@"exec ""{toolFilePath}"" {commandLineArguments}"; - internal static bool IsCoreClrRuntime => #if NET true; @@ -31,15 +25,24 @@ internal static string GetDotNetExecCommandLine(string toolFilePath, string comm false; #endif + internal const string DotNetRootEnvironmentName = "DOTNET_ROOT"; private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; private const string DotNetExperimentalHostPathEnvironmentName = "DOTNET_EXPERIMENTAL_HOST_PATH"; /// - /// Get the path to the dotnet executable. In the case the .NET SDK did not provide this information - /// in the environment this tries to find "dotnet" on the PATH. In the case it is not found, - /// this will return simply "dotnet". + /// The DOTNET_ROOT that should be used when launching executable tools. /// - internal static string GetDotNetPathOrDefault() + internal static string? GetToolDotNetRoot() + { + if (GetDotNetHostPath() is { } dotNetHostPath) + { + return Path.GetDirectoryName(dotNetHostPath); + } + + return null; + } + + private static string? GetDotNetHostPath() { if (Environment.GetEnvironmentVariable(DotNetHostPathEnvironmentName) is { Length: > 0 } pathToDotNet) { @@ -51,6 +54,21 @@ internal static string GetDotNetPathOrDefault() return pathToDotNetExperimental; } + return null; + } + + /// + /// Get the path to the dotnet executable. In the case the .NET SDK did not provide this information + /// in the environment this tries to find "dotnet" on the PATH. In the case it is not found, + /// this will return simply "dotnet". + /// + internal static string GetDotNetPathOrDefault() + { + if (GetDotNetHostPath() is { } pathToDotNet) + { + return pathToDotNet; + } + var (fileName, sep) = PlatformInformation.IsWindows ? ("dotnet.exe", new char[] { ';' }) : ("dotnet", new char[] { ':' }); @@ -74,5 +92,8 @@ internal static string GetDotNetPathOrDefault() return fileName; } + + internal static string GetDotNetExecCommandLine(string toolFilePath, string commandLineArguments) => + $@"exec ""{toolFilePath}"" {commandLineArguments}"; } } diff --git a/src/roslyn/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj b/src/roslyn/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj index 1550a45facb..96908065e36 100644 --- a/src/roslyn/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj +++ b/src/roslyn/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj @@ -6,10 +6,12 @@ true $(NetRoslynAll);net472 false - true Microsoft.CodeAnalysis.Test.Utilities - This is an internal package for testing. Not meant for external or production uses. The API can and will break at our discretion. true + + + true + This is an internal package for testing. Not meant for external or production uses. The API can and will break at our discretion. diff --git a/src/roslyn/src/Compilers/Test/Utilities/CSharp/TestSources.cs b/src/roslyn/src/Compilers/Test/Utilities/CSharp/TestSources.cs index 6214eca453e..32bd3d098e1 100644 --- a/src/roslyn/src/Compilers/Test/Utilities/CSharp/TestSources.cs +++ b/src/roslyn/src/Compilers/Test/Utilities/CSharp/TestSources.cs @@ -24,12 +24,14 @@ public readonly ref struct Span unsafe public Span(void* pointer, int length) { this.arr = Helpers.ToArray(pointer, length); + this.start = 0; this.Length = length; } public Span(T[] arr) { this.arr = arr; + this.start = 0; this.Length = arr is null ? 0 : arr.Length; } @@ -96,6 +98,8 @@ public ref T Current public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(span.arr); public Span Slice(int offset, int length) => new Span(this.arr, offset, length); + + public Span Slice(int offset) => new Span(this.arr, offset, Length - offset); } public readonly ref struct ReadOnlySpan @@ -111,12 +115,14 @@ public readonly ref struct ReadOnlySpan unsafe public ReadOnlySpan(void* pointer, int length) { this.arr = Helpers.ToArray(pointer, length); + this.start = 0; this.Length = length; } public ReadOnlySpan(T[] arr) { this.arr = arr; + this.start = 0; this.Length = arr is null ? 0 : arr.Length; } @@ -185,6 +191,8 @@ public ref readonly T Current public ReadOnlySpan Slice(int offset, int length) => new ReadOnlySpan(this.arr, offset, length); + public ReadOnlySpan Slice(int offset) => new ReadOnlySpan(this.arr, offset, offset - Length); + #nullable enable public static ReadOnlySpan CastUp(ReadOnlySpan items) where TDerived : class?, T { @@ -556,5 +564,15 @@ public static bool SequenceEqual (this Span span, ReadOnlySpan other) w public static Span AsSpan(this T[] array) => new Span(array); } }"; + + public static readonly string ParamsCollectionAttribute = """ + namespace System.Runtime.CompilerServices + { + public sealed class ParamCollectionAttribute : Attribute + { + public ParamCollectionAttribute() { } + } + } + """; } } diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index 46e90c7caa5..47ff89d6a4b 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -943,7 +943,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic For Each tree As SyntaxTree In trees If tree Is Nothing Then - Throw New ArgumentNullException(String.Format(VBResources.Trees0, i)) + Throw New ArgumentNullException($"trees({i})") End If If Not tree.HasCompilationUnitRoot Then @@ -955,7 +955,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If If declMap.ContainsKey(tree) Then - Throw New ArgumentException(VBResources.SyntaxTreeAlreadyPresent, String.Format(VBResources.Trees0, i)) + Throw New ArgumentException(VBResources.SyntaxTreeAlreadyPresent, $"trees({i})") End If AddSyntaxTreeToDeclarationMapAndTable(tree, _options, Me.IsSubmission, declMap, declTable, referenceDirectivesChanged) ' declMap and declTable passed ByRef diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb index b4bbdff84ad..cc063449a37 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/ErrorFacts.vb @@ -1549,7 +1549,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERRID.ERR_LockTypeUnsupported, ERRID.WRN_ConvertingLock, ERRID.ERR_EmbeddedAttributeMustFollowPattern, - ERRID.ERR_MethodImplAttributeAsyncCannotBeUsed + ERRID.ERR_MethodImplAttributeAsyncCannotBeUsed, + ERRID.ERR_AttributeCannotBeAppliedManually Return False Case Else ' NOTE: All error codes must be explicitly handled in the below select case statement diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index b841d19f114..b51ed40b56a 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1790,8 +1790,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_TooManyUserStrings_RestartRequired = 37336 ERR_MethodImplAttributeAsyncCannotBeUsed = 37337 + ERR_AttributeCannotBeAppliedManually = 37338 - ERR_NextAvailable = 37338 + ERR_NextAvailable = 37339 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb index 059e606cfba..e24e681641e 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplay.vb @@ -135,6 +135,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim visitor = SymbolDisplayVisitor.GetInstance(builder, format, semanticModelOpt, positionOpt) symbol.Accept(visitor) + + If TypeOf symbol Is INamedTypeSymbol AndAlso + DirectCast(symbol, INamedTypeSymbol).IsExtension Then + + visitor.AddExtensionMarkerName(DirectCast(symbol, INamedTypeSymbol)) + End If + visitor.Free() End Sub diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb index 4122c175253..1f4e0d136a1 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.vb @@ -160,12 +160,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If containingType IsNot Nothing Then visitedParents = True containingType.Accept(Me.NotFirstVisitor()) - - If Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes) Then - AddOperator(SyntaxKind.PlusToken) - Else - AddOperator(SyntaxKind.DotToken) - End If + AddNestedTypeSeparator() End If End If @@ -194,6 +189,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If End Sub + Private Sub AddNestedTypeSeparator() + If Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UsePlusForNestedTypes) Then + AddOperator(SyntaxKind.PlusToken) + Else + AddOperator(SyntaxKind.DotToken) + End If + End Sub + Private Function CanShowDelegateSignature(symbol As INamedTypeSymbol) As Boolean Return IsFirstSymbolVisited AndAlso symbol.TypeKind = TypeKind.Delegate AndAlso @@ -239,7 +242,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If If symbolName Is Nothing Then - symbolName = If(symbol.IsExtension, symbol.MetadataName, symbol.Name) + symbolName = If(symbol.IsExtension, symbol.ExtensionGroupingName, symbol.Name) End If If Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName) AndAlso @@ -578,5 +581,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If End Sub + Friend Sub AddExtensionMarkerName(extension As INamedTypeSymbol) + Debug.Assert(extension.IsExtension) + AddNestedTypeSeparator() + Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, extension, extension.ExtensionMarkerName, False)) + End Sub End Class End Namespace diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb index 7acc4a1a228..c85130b736d 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/MethodSymbol.vb @@ -1236,6 +1236,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return visitor.VisitMethod(Me) End Function + Public ReadOnly Property AssociatedExtensionImplementation As IMethodSymbol Implements IMethodSymbol.AssociatedExtensionImplementation + Get + Return Nothing + End Get + End Property + #End Region End Class diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb index dc4e5306965..830c3fd04b6 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/NamedTypeSymbol.vb @@ -1569,5 +1569,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return False End Function + Public ReadOnly Property IsExtension As Boolean Implements INamedTypeSymbol.IsExtension + Get + Return False + End Get + End Property + + Public ReadOnly Property ExtensionParameter As IParameterSymbol Implements INamedTypeSymbol.ExtensionParameter + Get + Return Nothing + End Get + End Property + + Public ReadOnly Property ExtensionGroupingName As String Implements INamedTypeSymbol.ExtensionGroupingName + Get + Return Nothing + End Get + End Property + + Public ReadOnly Property ExtensionMarkerName As String Implements INamedTypeSymbol.ExtensionMarkerName + Get + Return Nothing + End Get + End Property End Class End Namespace diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb index 0e8ce54e2f2..ee08be48d11 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/Symbol_Attributes.vb @@ -210,6 +210,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim attrArgumentLocation = VisualBasicAttributeData.GetFirstArgumentLocation(arguments.AttributeSyntaxOpt) DirectCast(arguments.Diagnostics, BindingDiagnosticBag).Add(ERRID.ERR_InvalidExperimentalDiagID, attrArgumentLocation) End If + ElseIf arguments.Attribute.IsTargetAttribute(AttributeDescription.MetadataUpdateDeletedAttribute) Then + arguments.Diagnostics.DiagnosticBag.Add(ERRID.ERR_AttributeCannotBeAppliedManually, arguments.AttributeSyntaxOpt.Location, AttributeDescription.MetadataUpdateDeletedAttribute.FullName) End If End Sub diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb index 18b9b207233..be406d80fcd 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb @@ -783,13 +783,15 @@ Done: Return Me End Function - Public ReadOnly Property IsExtension As Boolean Implements ITypeSymbol.IsExtension + + Private ReadOnly Property ITypeSymbol_IsExtension As Boolean Implements ITypeSymbol.IsExtension Get Return False End Get End Property - Public ReadOnly Property ExtensionParameter As IParameterSymbol Implements ITypeSymbol.ExtensionParameter + + Private ReadOnly Property ITypeSymbol_ExtensionParameter As IParameterSymbol Implements ITypeSymbol.ExtensionParameter Get Return Nothing End Get diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/roslyn/src/Compilers/VisualBasic/Portable/VBResources.resx index b087d43e35c..78beae0d799 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5738,6 +5738,9 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. - 'MethodImplAttribute.Async' is not localizable. + 'MethodImplAttribute.Async' is not localizable. + + + '{0}' cannot be applied manually. diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf index 7ec63ae72fa..10fdf83e75f 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf @@ -7,6 +7,11 @@ Vlastnost init-only {0} může být přiřazena jen inicializátorem člena objektu, nebo pro Me, MyClass nebo MyBase v konstruktoru instance. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Nelze získat přístup ke sdílenému abstraktnímu nebo virtuálnímu členu rozhraní. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf index 44570477f1c..bab2a820f76 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf @@ -7,6 +7,11 @@ Die init-only-Eigenschaft "{0}" kann nur durch einen Initialisierer für Objektmember bzw. bei "Me", "MyClass" oder "MyBase" in einem Instanzkonstruktor zugewiesen werden. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Auf einen freigegebenen abstrakten oder virtuellen Schnittstellenmember kann nicht zugegriffen werden. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf index f38355446f1..341f1804d09 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf @@ -7,6 +7,11 @@ Solo un inicializador de miembro de objeto puede asignar la propiedad de solo inicio "{0}", o bien en "Me", "MyClass" o "MyBase" en un constructor de instancia. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. No se puede tener acceso a un miembro de interfaz virtual o abstracto compartido. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf index 8477461c443..732288f4fdc 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf @@ -7,6 +7,11 @@ La propriété d'initialisation uniquement '{0}' peut uniquement être affectée par un initialiseur de membre d'objet, ou sur 'Me', 'MyClass` ou 'MyBase' dans un constructeur d'instance. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Impossible d’accéder à un membre d’interface virtuelle ou abstrait partagé. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf index 884ae0525e7..12db20416f8 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf @@ -7,6 +7,11 @@ La proprietà di sola inizializzazione '{0}' può essere assegnata solo da un inizializzatore di membro di oggetto oppure in 'Me', 'MyClass' o 'MyBase' in un costruttore di istanza. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Impossibile accedere a un membro di interfaccia astratto o virtuale condiviso. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf index b40fbfc8c2b..345b1647f8a 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf @@ -7,6 +7,11 @@ 初期化専用プロパティ '{0}' は、オブジェクト メンバー初期化子を使用する場合か、インスタンス コンストラクター内の 'Me'、'MyClass'、'MyBase' でのみ割り当てることができます。 + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. 共有された抽象または仮想インターフェイスメンバーにアクセスできません。 @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf index 080df53ac6b..0ffcfc36f9e 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf @@ -7,6 +7,11 @@ 초기값 전용 속성 '{0}'은(는) 개체 멤버 이니셜라이저에 의해서나 인스턴스 생성자의 'Me', 'MyClass` 또는 'MyBase'에만 할당할 수 있습니다. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. 공유 추상 또는 가상 인터페이스 구성원에 액세스할 수 없습니다. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf index 80fa5fd0da9..f55d0b91bbc 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf @@ -7,6 +7,11 @@ Właściwość tylko do inicjowania „{0}” może być przypisana tylko przez inicjatora składowej obiektu lub w elemencie „Me”, „MyClass” lub „MyBase” w konstruktorze wystąpień. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Nie można uzyskać dostępu do współużytkowanego abstrakcyjnego elementu członkowskiego interfejsu. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf index a5d46658733..d5badf9a6ab 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf @@ -7,6 +7,11 @@ A propriedade apenas para inicialização '{0}' só pode ser atribuída por um inicializador de membro de objeto ou em 'Me', 'MyClass` ou 'MyBase' em um construtor de instância. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Um membro de interface com abstrato compartilhado ou virtual não pode ser acessado. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf index c2230606747..185fbcc9f87 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf @@ -7,6 +7,11 @@ Свойство "{0}", доступное только для инициализации, можно назначать только c помощью инициализатора элемента объекта или для "Me", "MyClass" или "MyBase" в конструкторе экземпляра. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Нет доступа к общему абстрактному или виртуальному элементу интерфейса. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf index a8b242c8e9c..8afee8547ef 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf @@ -7,6 +7,11 @@ '{0}' init-only özelliği yalnızca nesne üyesi başlatıcısı tarafından veya örnek oluşturucudaki 'Me', 'MyClass' veya 'MyBase' üzerinde atanabilir. + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. Paylaşılan bir soyut veya sanal arabirim üyesine erişilemiyor. @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf index eb11ccf14db..94632aeaba2 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf @@ -7,6 +7,11 @@ Init only 属性 "{0}" 只能由对象成员初始值设定项分配,或在实例构造函数中的 "Me"、"MyClass" 或 "MyBase" 上分配。 + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. 无法访问共享抽象或虚拟接口成员。 @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf index 7f9fcf504c0..ec61d39e171 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf +++ b/src/roslyn/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf @@ -7,6 +7,11 @@ 只有物件成員初始設定式,或在執行個體建構函式中的 'Me'、'MyClass' 或 'MyBase',才可指派僅限初始化的屬性 '{0}'。 + + '{0}' cannot be applied manually. + '{0}' cannot be applied manually. + + A shared abstract or virtual interface member cannot be accessed. 無法存取共用摘要或虛擬介面成員。 @@ -75,7 +80,7 @@ 'MethodImplAttribute.Async' cannot be manually applied to methods. 'MethodImplAttribute.Async' cannot be manually applied to methods. - + 'MethodImplAttribute.Async' is not localizable. Multiple analyzer config files cannot be in the same directory ('{0}'). diff --git a/src/roslyn/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb b/src/roslyn/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb index a8bb9bd6447..e4c0b6e4813 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Test/Emit/Attributes/AttributeTests.vb @@ -3224,6 +3224,111 @@ Public Delegate Function D(a As Integer, ByRef b As Integer) As Intege CompileAndVerify(source, sourceSymbolValidator:=attributeValidator, symbolValidator:=attributeValidator) End Sub + + Public Sub MetadataUpdateDeletedAttribute_ErrorWhenManuallyApplied() + Dim source As String = " + + + +Namespace System.Runtime.CompilerServices + Class TestClass(Of T) + Sub New() + field = 1 + RaiseEvent E1() + End Sub + + Public field As Integer + + Property P As Integer + Get + Return 1 + End Get + Set + End Set + End Property + + ReadOnly Property Item(a As Integer) As Integer + Get + Return a + End Get + End Property + + Function M( x As Integer) _ + As Integer + Return x + End Function + + Public Event E1 As Action + + Public Custom Event E2 As Action + AddHandler(value As Action) : End AddHandler + RemoveHandler(value As Action) : End RemoveHandler + RaiseEvent() : End RaiseEvent + End Event + End Class + + + Public NotInheritable Class MetadataUpdateDeletedAttribute + Inherits Attribute + End Class +End Namespace +" + Dim compilation = CreateCompilation(source) + compilation.AssertTheseDiagnostics( +"BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Class TestClass(Of T) + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Sub New() + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Public field As Integer + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Property P As Integer + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Get + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Set + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + ReadOnly Property Item(a As Integer) As Integer + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Function M( x As Integer) _ + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Function M( x As Integer) _ + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + As Integer + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Public Event E1 As Action + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + Public Custom Event E2 As Action + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + AddHandler(value As Action) : End AddHandler + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + RemoveHandler(value As Action) : End RemoveHandler + ~~~~~~~~~~~~~~~~~~~~~ +BC37338: 'System.Runtime.CompilerServices.MetadataUpdateDeletedAttribute' cannot be applied manually. + RaiseEvent() : End RaiseEvent + ~~~~~~~~~~~~~~~~~~~~~ +") + End Sub + #End Region ''' diff --git a/src/roslyn/src/Compilers/VisualBasic/Test/Emit/ExpressionTrees/CodeGenExprLambda.vb b/src/roslyn/src/Compilers/VisualBasic/Test/Emit/ExpressionTrees/CodeGenExprLambda.vb index dc7e20aff41..a8ec66b14fa 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Test/Emit/ExpressionTrees/CodeGenExprLambda.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Test/Emit/ExpressionTrees/CodeGenExprLambda.vb @@ -8806,6 +8806,81 @@ End Class expectedOutput:=).VerifyDiagnostics() + + End Sub + + + + Public Sub ConstrainedTypeParameter() + + Dim source = + + + + CompileAndVerify(source, + options:=TestOptions.DebugExe, + expectedOutput:=).VerifyDiagnostics() End Sub diff --git a/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb b/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb index de3ea903da8..f4405372c47 100644 --- a/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb +++ b/src/roslyn/src/Compilers/VisualBasic/Test/Symbol/SymbolDisplay/SymbolDisplayTests.vb @@ -6066,20 +6066,22 @@ static class E End If Dim e = DirectCast(comp.GlobalNamespace.GetMembers("E").Single(), ITypeSymbol) - Dim extension = e.GetMembers().OfType(Of ITypeSymbol).Single() + Dim extension = e.GetMembers().OfType(Of INamedTypeSymbol).Single() Assert.True(extension.IsExtension) - AssertEx.Equal("E.$119AA281C143547563250CAF89B48A76", SymbolDisplay.ToDisplayString(extension, format)) + AssertEx.Equal("E.$C43E2675C7BBF9284AF22FB8A9BF0280.$119AA281C143547563250CAF89B48A76", SymbolDisplay.ToDisplayString(extension, format)) Dim parts = SymbolDisplay.ToDisplayParts(extension, format) Verify(parts, - "E.$119AA281C143547563250CAF89B48A76", + "E.$C43E2675C7BBF9284AF22FB8A9BF0280.$119AA281C143547563250CAF89B48A76", + SymbolDisplayPartKind.ClassName, + SymbolDisplayPartKind.Operator, SymbolDisplayPartKind.ClassName, SymbolDisplayPartKind.Operator, SymbolDisplayPartKind.ClassName) Dim skeletonM = extension.GetMembers("M").Single() - AssertEx.Equal("Public Sub E.$119AA281C143547563250CAF89B48A76.M()", SymbolDisplay.ToDisplayString(skeletonM, format)) + AssertEx.Equal("Public Sub E.$C43E2675C7BBF9284AF22FB8A9BF0280.M()", SymbolDisplay.ToDisplayString(skeletonM, format)) End Sub @@ -6121,15 +6123,14 @@ static class E End If Dim e = DirectCast(comp.GlobalNamespace.GetMembers("E").Single(), ITypeSymbol) - Dim extension = e.GetMembers().OfType(Of ITypeSymbol).Single() + Dim extension = e.GetMembers().OfType(Of INamedTypeSymbol).Single() - ' Tracked by https://github.com/dotnet/roslyn/issues/78957 : public API, the arity should not be included in the extension type name Assert.True(extension.IsExtension) - AssertEx.Equal("E.$D1693D81A12E8DED4ED68FE22D9E856F(Of T)", SymbolDisplay.ToDisplayString(extension, format)) + AssertEx.Equal("E.$8048A6C8BE30A622530249B904B537EB(Of T).$D1693D81A12E8DED4ED68FE22D9E856F", SymbolDisplay.ToDisplayString(extension, format)) Dim parts = SymbolDisplay.ToDisplayParts(extension, format) Verify(parts, - "E.$D1693D81A12E8DED4ED68FE22D9E856F(Of T)", + "E.$8048A6C8BE30A622530249B904B537EB(Of T).$D1693D81A12E8DED4ED68FE22D9E856F", SymbolDisplayPartKind.ClassName, SymbolDisplayPartKind.Operator, SymbolDisplayPartKind.ClassName, @@ -6137,10 +6138,12 @@ static class E SymbolDisplayPartKind.Keyword, SymbolDisplayPartKind.Space, SymbolDisplayPartKind.TypeParameterName, - SymbolDisplayPartKind.Punctuation) + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Operator, + SymbolDisplayPartKind.ClassName) Dim skeletonM = extension.GetMembers("M").Single() - AssertEx.Equal("Public Sub E.$D1693D81A12E8DED4ED68FE22D9E856F(Of T).M()", SymbolDisplay.ToDisplayString(skeletonM, format)) + AssertEx.Equal("Public Sub E.$8048A6C8BE30A622530249B904B537EB(Of T).M()", SymbolDisplay.ToDisplayString(skeletonM, format)) End Sub #Region "Helpers" diff --git a/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj b/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj index f4abf997885..7a2f3558bbf 100644 --- a/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj +++ b/src/roslyn/src/Compilers/VisualBasic/vbc/AnyCpu/vbc.csproj @@ -4,8 +4,8 @@ Exe $(NetRoslynSourceBuild);net472 - false true + false diff --git a/src/roslyn/src/Dependencies/Threading/IAsyncEnumerableExtensions.cs b/src/roslyn/src/Dependencies/Threading/IAsyncEnumerableExtensions.cs index 9d0447c6852..9e708791a00 100644 --- a/src/roslyn/src/Dependencies/Threading/IAsyncEnumerableExtensions.cs +++ b/src/roslyn/src/Dependencies/Threading/IAsyncEnumerableExtensions.cs @@ -23,6 +23,7 @@ public static async Task> ToImmutableArrayAsync(this IAsync return result.ToImmutableAndClear(); } + // Remove after .NET 10, https://github.com/dotnet/roslyn/issues/80198 #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods public static async IAsyncEnumerable AsAsyncEnumerable(this IEnumerable source) diff --git a/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs b/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs index c84d5a54ffc..1ad65b2df0d 100644 --- a/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs +++ b/src/roslyn/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs @@ -144,7 +144,7 @@ async Task TryExecuteCommandAsync() { _threadingContext.ThrowIfNotOnUIThread(); - var factory = document.Project.Solution.Workspace.Services.GetRequiredService(); + var factory = document.Project.Solution.Services.GetRequiredService(); using var waitContext = factory.Create( textView, applicableToSpan, diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs index 0234b4b8bb6..e1acae43702 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests_Regex.cs @@ -1290,4 +1290,32 @@ void Bar([StringSyntax(StringSyntaxAttribute.Regex)] string p) } """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, testHost); + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/80179")] + public Task TestRegexOnApiWithStringSyntaxAttribute_ParamsReadOnlyCollectionArgument(TestHost testHost) + => TestAsync( + """ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Text.RegularExpressions; + + class Program + { + private void M([StringSyntax(StringSyntaxAttribute.Regex)] params IReadOnlyCollection p) + { + } + + void Goo() + { + [|M([@"$\a(?#comment)"]);|] + } + } + """ + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, + testHost, + Method("M"), + Regex.Anchor("$"), + Regex.OtherEscape("\\"), + Regex.OtherEscape("a"), + Regex.Comment("(?#comment)")); } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs index 49ebd1d6c6e..fcaf52afac6 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests_ExtensionMethods.cs @@ -2,15 +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.Threading.Tasks; using Microsoft.CodeAnalysis.Remote.Testing; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddUsing; +[Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] public sealed partial class AddUsingTests { [Fact] @@ -1508,4 +1508,265 @@ public static T Bar( this Goo @this ) } } """); + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80240")] + [InlineData("object")] + [InlineData("int")] + public Task TestModernExtension_Method(string sourceType) + => TestInRegularAndScriptAsync( + $$""" + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + [|o.M1();|] + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public void M1() + { + } + } + } + } + """, + $$""" + using N; + + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + o.M1(); + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public void M1() + { + } + } + } + } + """); + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80240")] + [InlineData("object")] + [InlineData("int")] + public Task TestModernExtension_StaticMethod(string sourceType) + => TestInRegularAndScriptAsync( + $$""" + namespace M + { + class C + { + void X() + { + [|{{sourceType}}.M1();|] + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public static void M1() + { + } + } + } + } + """, + $$""" + using N; + + namespace M + { + class C + { + void X() + { + {{sourceType}}.M1(); + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public static void M1() + { + } + } + } + } + """); + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80240")] + [InlineData("object")] + [InlineData("int")] + public Task TestModernExtension_Property(string sourceType) + => TestInRegularAndScriptAsync( + $$""" + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + var v = [|o.M1;|] + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public int M1 => 0; + } + } + } + """, + $$""" + using N; + + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + var v = o.M1; + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public int M1 => 0; + } + } + } + """); + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80240")] + [InlineData("object")] + [InlineData("int")] + public Task TestModernExtension_StaticProperty(string sourceType) + => TestInRegularAndScriptAsync( + $$""" + namespace M + { + class C + { + void X() + { + [|{{sourceType}}.M1;|] + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public static int M1 => 0; + } + } + } + """, + $$""" + using N; + + namespace M + { + class C + { + void X() + { + {{sourceType}}.M1; + } + } + } + + namespace N + { + static class Test + { + extension(object o) + { + public static int M1 => 0; + } + } + } + """); + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/80240")] + [InlineData("object")] + [InlineData("int")] + public Task TestModernExtension_TypeParameter(string sourceType) + // Will work once we get an API from https://github.com/dotnet/roslyn/issues/80273 + => TestMissingAsync( + $$""" + namespace M + { + class C + { + void X() + { + {{sourceType}} o = new(); + [|o.M1();|] + } + } + } + + namespace N + { + static class Test + { + extension(T t) + { + public void M1() + { + } + } + } + } + """); } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs index d9bd2d2acc4..bb5b2208f17 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs @@ -52,7 +52,7 @@ protected override Task CreateWorkspaceImplAsync() } private static OptionsCollection PreferImplicitTypeWithInfo() - => new OptionsCollection(LanguageNames.CSharp) + => new(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarElsewhere, true, NotificationOption2.Suggestion }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, true, NotificationOption2.Suggestion }, @@ -60,7 +60,7 @@ private static OptionsCollection PreferImplicitTypeWithInfo() }; private static OptionsCollection PreferExplicitTypeWithInfo() - => new OptionsCollection(LanguageNames.CSharp) + => new(LanguageNames.CSharp) { { CSharpCodeStyleOptions.VarElsewhere, false, NotificationOption2.Suggestion }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, false, NotificationOption2.Suggestion }, diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs index ad2dab6115a..02cec1878e0 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromParameterTests.cs @@ -1454,9 +1454,9 @@ public C([|string p__End, string p_test_t|]) } """, parameters: new TestParameters(options: options.MergeStyles(options.PropertyNamesArePascalCase, options.ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffix))); - private TestParameters OmitIfDefault_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); - private TestParameters Never_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); - private TestParameters Always_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); + private TestParameters OmitIfDefault_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); + private TestParameters Never_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); + private TestParameters Always_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); [Fact] public Task TestCreateFieldWithTopLevelNullability() diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs index c23727189c2..f16e876670f 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/InitializeMemberFromPrimaryConstructorParameterTests.cs @@ -23,9 +23,9 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorT private readonly NamingStylesTestOptionSets _options = new(LanguageNames.CSharp); - private TestParameters OmitIfDefault_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); - private TestParameters Never_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); - private TestParameters Always_Warning => new TestParameters(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); + private TestParameters OmitIfDefault_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.OmitIfDefault, NotificationOption2.Warning)); + private TestParameters Never_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Warning)); + private TestParameters Always_Warning => new(options: Option(CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Always, NotificationOption2.Warning)); [Fact] public Task TestInitializeFieldWithSameName() diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs index d943b9e2b3c..7ec4b930146 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/MoveStaticMembers/CSharpMoveStaticMembersTests.cs @@ -792,6 +792,53 @@ public static int TestMethod() """, "Class1Helpers.cs", selectedMembers, "Class1Helpers").ConfigureAwait(false); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79806")] + public async Task TestMoveStaticMethodWithStaticMembers() + { + var selectedMembers = ImmutableArray.Create("StaticMethod"); + + await TestMovementNewFileAsync(""" + using System; + + namespace TestNs1 + { + internal class ClassWithStaticMembers + { + public static int StaticInt { get; set; } + public static string StaticString { get; set; } + public static void Static[||]Method() + { + Console.WriteLine(StaticString + StaticInt); + } + } + } + """, """ + using System; + + namespace TestNs1 + { + internal class ClassWithStaticMembers + { + public static int StaticInt { get; set; } + public static string StaticString { get; set; } + } + } + """, """ + using System; + + namespace TestNs1 + { + internal static class ClassWithStaticMembersHelpers + { + public static void StaticMethod() + { + Console.WriteLine(ClassWithStaticMembers.StaticString + ClassWithStaticMembers.StaticInt); + } + } + } + """, "ClassWithStaticMembersHelpers.cs", selectedMembers, "ClassWithStaticMembersHelpers").ConfigureAwait(false); + } + [Fact] public async Task TestMoveMethodAndRefactorUsageWithTrivia() { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs index 7a9ced1875e..8787655b88b 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/CodeActions/PreviewExceptionTests.cs @@ -87,14 +87,14 @@ private static async Task ActionSets(EditorTestWorkspace workspace, CodeRefactor Assert.False(extensionManager.IsIgnored(provider)); } - private static CodeRefactoringSuggestedAction CreateRefactoringSuggestedAction(EditorTestWorkspace workspace, CodeRefactoringProvider provider, out EditorLayerExtensionManager.ExtensionManager extensionManager) + private static EditorSuggestedActionWithNestedFlavors CreateRefactoringSuggestedAction(EditorTestWorkspace workspace, CodeRefactoringProvider provider, out EditorLayerExtensionManager.ExtensionManager extensionManager) { var codeActions = new List(); RefactoringSetup(workspace, provider, codeActions, out extensionManager, out var textBuffer, out var document); - var suggestedAction = new CodeRefactoringSuggestedAction( + var suggestedAction = new EditorSuggestedActionWithNestedFlavors( workspace.ExportProvider.GetExportedValue(), workspace.ExportProvider.GetExportedValue(), - workspace, document, textBuffer, provider, codeActions.First(), fixAllFlavors: null); + document, textBuffer, provider, codeActions.First(), fixAllFlavors: null, diagnostics: []); return suggestedAction; } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs index 63781d52ca3..4363e0c3fae 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ObjectInitializerCompletionProviderTests.cs @@ -1093,6 +1093,29 @@ static void Main(string[] args) await VerifyItemExistsAsync(markup, "RequiredProperty", inlineDescription: FeaturesResources.Required); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80192")] + public async Task RequiredMembersNotLabeledOrSelectedInRecordWith() + { + var markup = """ + record C + { + public required int RequiredField; + public required int RequiredProperty { get; set; } + } + + class D + { + static void Main(C c) + { + var t = c with { $$ }; + } + } + """; + + await VerifyItemExistsAsync(markup, "RequiredField", inlineDescription: "", matchPriority: MatchPriority.Default); + await VerifyItemExistsAsync(markup, "RequiredProperty", inlineDescription: ""); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/15205")] public Task NestedPropertyInitializers1() => VerifyItemExistsAsync(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 066e670df8d..ecf1d51565a 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -179,7 +179,7 @@ public async Task AnalyzerOptionsArePassedToAllAnalyzers() private static void AccessSupportedDiagnostics(DiagnosticAnalyzer analyzer) { var diagnosticService = new HostDiagnosticAnalyzers([new AnalyzerImageReference([analyzer])]); - diagnosticService.GetDiagnosticDescriptorsPerReference(new DiagnosticAnalyzerInfoCache()); + diagnosticService.GetDiagnosticDescriptorsPerReference(new DiagnosticAnalyzerInfoCache(), project: null); } private sealed class ThrowingDoNotCatchDiagnosticAnalyzer : ThrowingDiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct @@ -214,7 +214,7 @@ private sealed class CompilationAnalyzerWithSyntaxTreeAnalyzer : DiagnosticAnaly private const string ID = "SyntaxDiagnostic"; private static readonly DiagnosticDescriptor s_syntaxDiagnosticDescriptor = - new DiagnosticDescriptor(ID, title: "Syntax", messageFormat: "Syntax", category: "Test", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + new(ID, title: "Syntax", messageFormat: "Syntax", category: "Test", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs index ce3d0e8006c..a66920b7240 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Diagnostics/MockDiagnosticAnalyzerTests.cs @@ -20,7 +20,7 @@ public sealed partial class MockDiagnosticAnalyzerTests : AbstractCSharpDiagnost private sealed class MockDiagnosticAnalyzer : DiagnosticAnalyzer { public const string Id = "MockDiagnostic"; - private readonly DiagnosticDescriptor _descriptor = new DiagnosticDescriptor(Id, "MockDiagnostic", "MockDiagnostic", "InternalCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://github.com/dotnet/roslyn"); + private readonly DiagnosticDescriptor _descriptor = new(Id, "MockDiagnostic", "MockDiagnostic", "InternalCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://github.com/dotnet/roslyn"); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs index 57fd3ab0d8b..8bb4d30f3c4 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupCommandHandlerTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EventHookup; [Trait(Traits.Feature, Traits.Features.EventHookup)] public sealed class EventHookupCommandHandlerTests { - private readonly NamingStylesTestOptionSets _namingOptions = new NamingStylesTestOptionSets(LanguageNames.CSharp); + private readonly NamingStylesTestOptionSets _namingOptions = new(LanguageNames.CSharp); [WpfFact] public async Task HandlerName_EventInThisClass() @@ -1203,5 +1203,5 @@ public void M2() } private static OptionsCollection QualifyMethodAccessWithNotification(NotificationOption2 notification) - => new OptionsCollection(LanguageNames.CSharp) { { CodeStyleOptions2.QualifyMethodAccess, true, notification } }; + => new(LanguageNames.CSharp) { { CodeStyleOptions2.QualifyMethodAccess, true, notification } }; } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs index b51f6979de1..a01dbc71102 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/EventHookup/EventHookupTestState.cs @@ -48,7 +48,7 @@ public EventHookupTestState(XElement workspaceElement, OptionsCollection options } public static EventHookupTestState CreateTestState(string markup, OptionsCollection options = null) - => new EventHookupTestState(GetWorkspaceXml(markup), options); + => new(GetWorkspaceXml(markup), options); public static XElement GetWorkspaceXml(string markup) => XElement.Parse(string.Format(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs index 2c4af692097..5a21a33eee9 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.TestFixers.cs @@ -121,22 +121,16 @@ private sealed class ModifySolutionFixAll : FixAllProvider } [PartNotDiscoverable, Shared, ExportCodeFixProvider(LanguageNames.CSharp)] - private sealed class TestThirdPartyCodeFixDoesNotSupportDocumentScope : TestThirdPartyCodeFix + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + private sealed class TestThirdPartyCodeFixDoesNotSupportDocumentScope() : TestThirdPartyCodeFix { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TestThirdPartyCodeFixDoesNotSupportDocumentScope() - { - } - public override FixAllProvider GetFixAllProvider() => new ModifySolutionFixAll(); private sealed class ModifySolutionFixAll : FixAllProvider { public override IEnumerable GetSupportedFixAllScopes() - { - return new[] { FixAllScope.Project, FixAllScope.Solution, FixAllScope.Custom }; - } + => [FixAllScope.Project, FixAllScope.Solution, FixAllScope.Custom]; public override Task GetFixAsync(FixAllContext fixAllContext) { diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index 10d6743608c..8467eae800a 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -414,10 +414,10 @@ private void Method() } } """, """ - using System; - namespace A { + using System; + internal class Program { private void Method() diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs index b1e058cbcc6..18b3038afc1 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/Interactive/NavigateTo/InteractiveNavigateToTests.cs @@ -501,11 +501,11 @@ static C2() { var expecteditems = new List { - new NavigateToItem("C1", NavigateToItemKind.Class, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), - new NavigateToItem("C1", NavigateToItemKind.Method, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), - new NavigateToItem("C2", NavigateToItemKind.Class, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), - new NavigateToItem("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), // this is the static ctor - new NavigateToItem("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), + new("C1", NavigateToItemKind.Class, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), + new("C1", NavigateToItemKind.Method, "csharp", "C1", null, s_emptyPrefixPatternMatch, null), + new("C2", NavigateToItemKind.Class, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), + new("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), // this is the static ctor + new("C2", NavigateToItemKind.Method, "csharp", "C2", null, s_emptyPrefixPatternMatch, null), }; var items = (await _aggregator.GetItemsAsync("C")).ToList(); items.Sort(CompareNavigateToItems); diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs index fe7fd8711e3..f2cdbac7279 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs @@ -161,10 +161,10 @@ public class C """, description, []); } - private static async Task AssertContentIsAsync(EditorTestWorkspace workspace, Document document, int position, string expectedDescription, + private static async Task AssertContentIsAsync(Document document, int position, string expectedDescription, ImmutableArray relatedSpans) { - var info = await GetQuickinfo(workspace, document, position); + var info = await GetQuickInfo(document, position); var description = info?.Sections.FirstOrDefault(s => s.Kind == QuickInfoSectionKinds.Description); Assert.NotNull(description); Assert.Equal(expectedDescription, description.Text); @@ -172,17 +172,16 @@ private static async Task AssertContentIsAsync(EditorTestWorkspace workspace, Do [.. info.RelatedSpans.Select(actualSpan => new Action(expectedSpan => Assert.Equal(expectedSpan, actualSpan)))]); } - private static async Task GetQuickinfo(EditorTestWorkspace workspace, Document document, int position) + private static async Task GetQuickInfo(Document document, int position) { - var sharedGlobalCache = workspace.ExportProvider.GetExportedValue(); - var provider = new CSharpDiagnosticAnalyzerQuickInfoProvider(sharedGlobalCache); + var provider = new CSharpDiagnosticAnalyzerQuickInfoProvider(); var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, SymbolDescriptionOptions.Default, CancellationToken.None)); return info; } - private static async Task AssertNoContentAsync(EditorTestWorkspace workspace, Document document, int position) + private static async Task AssertNoContentAsync(Document document, int position) { - var info = await GetQuickinfo(workspace, document, position); + var info = await GetQuickInfo(document, position); Assert.Null(info); } @@ -201,11 +200,11 @@ private static async Task TestAsync( var document = workspace.CurrentSolution.Projects.First().Documents.First(); if (string.IsNullOrEmpty(expectedDescription)) { - await AssertNoContentAsync(workspace, document, position); + await AssertNoContentAsync(document, position); } else { - await AssertContentIsAsync(workspace, document, position, expectedDescription, relatedSpans); + await AssertContentIsAsync(document, position, expectedDescription, relatedSpans); } } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.cs index 0fee9ebb330..44247c09e6a 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; @@ -11,7 +14,15 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; public abstract class AbstractCSharpSignatureHelpProviderTests : AbstractSignatureHelpProviderTests { protected override ParseOptions CreateExperimentalParseOptions() + => new CSharpParseOptions().WithFeatures([]); // no experimental features to enable + + protected override Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + IEnumerable? expectedOrderedItemsOrNull = null, + bool usePreviousCharAsTrigger = false, + SourceCodeKind? sourceCodeKind = null, + bool experimental = false) { - return new CSharpParseOptions().WithFeatures([]); // no experimental features to enable + return base.TestAsync(markup, expectedOrderedItemsOrNull, usePreviousCharAsTrigger, sourceCodeKind, experimental); } } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs index 38425d1f981..6b8ccee8cad 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs @@ -26,11 +26,6 @@ internal override Type GetSignatureHelpProviderType() [Fact] public async Task TestInvocationWithoutParameters() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -40,17 +35,13 @@ class SomethingAttribute : System.Attribute class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithoutParametersMethodXmlComments() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute()", "Summary For Attribute", null, currentParameterIndex: 0) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -62,18 +53,13 @@ public SomethingAttribute() { } class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute()", "Summary For Attribute", null, currentParameterIndex: 0)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25830")] public async Task PickCorrectOverload_PickInt() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute(int i)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("SomethingAttribute(string i)", currentParameterIndex: 0), - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -83,18 +69,14 @@ class SomethingAttribute : System.Attribute } [[|Something(i: 1$$|])] class D { } - """, expectedOrderedItems); + """, [ + new("SomethingAttribute(int i)", currentParameterIndex: 0, isSelected: true), + new("SomethingAttribute(string i)", currentParameterIndex: 0)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25830")] public async Task PickCorrectOverload_PickString() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("SomethingAttribute(string i)", currentParameterIndex: 0, isSelected: true), - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -104,17 +86,14 @@ class SomethingAttribute : System.Attribute } [[|Something(i: null$$|])] class D { } - """, expectedOrderedItems); + """, [ + new("SomethingAttribute(int i)", currentParameterIndex: 0), + new("SomethingAttribute(string i)", currentParameterIndex: 0, isSelected: true)]); } [Fact] public async Task TestInvocationWithParametersOn1() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -125,17 +104,13 @@ public SomethingAttribute(int someInteger, string someString) { } class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithParametersXmlCommentsOn1() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someInteger", currentParameterIndex: 0) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -151,17 +126,13 @@ public SomethingAttribute(int someInteger, string someString) { } |]class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someInteger", currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithParametersOn2() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -172,17 +143,13 @@ public SomethingAttribute(int someInteger, string someString) { } class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute(int someInteger, string someString)", string.Empty, string.Empty, currentParameterIndex: 1)]); } [Fact] public async Task TestInvocationWithParametersXmlComentsOn2() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someString", currentParameterIndex: 1) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -198,17 +165,13 @@ public SomethingAttribute(int someInteger, string someString) { } |]class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute(int someInteger, string someString)", "Summary For Attribute", "Param someString", currentParameterIndex: 1)]); } [Fact] public async Task TestInvocationWithClosingParen() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) - }; - await TestAsync(""" class SomethingAttribute : System.Attribute { } @@ -217,7 +180,8 @@ class SomethingAttribute : System.Attribute class D { } - """, expectedOrderedItems); + """, + [new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0)]); } [Fact] @@ -297,7 +261,7 @@ public async Task TestAttributeWithValidField() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) + new($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -318,7 +282,7 @@ public async Task TestAttributeWithInvalidFieldReadonly() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -339,7 +303,7 @@ public async Task TestAttributeWithInvalidFieldStatic() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -360,7 +324,7 @@ public async Task TestAttributeWithInvalidFieldConst() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -386,7 +350,7 @@ public async Task TestAttributeWithValidProperty() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) + new($"SomethingAttribute({FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 0) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -408,7 +372,7 @@ public async Task TestAttributeWithInvalidPropertyStatic() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -429,7 +393,7 @@ public async Task TestAttributeWithInvalidPropertyNoSetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -450,7 +414,7 @@ public async Task TestAttributeWithInvalidPropertyNoGetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -471,7 +435,7 @@ public async Task TestAttributeWithInvalidPropertyPrivateGetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -492,7 +456,7 @@ public async Task TestAttributeWithInvalidPropertyPrivateSetter() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) + new("SomethingAttribute()", string.Empty, null, currentParameterIndex: 0) }; await TestAsync(""" @@ -514,7 +478,7 @@ public async Task TestAttributeWithOverriddenProperty() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"DerivedAttribute({FeaturesResources.Properties}: [Name = string])", string.Empty, string.Empty, currentParameterIndex: 0) + new($"DerivedAttribute({FeaturesResources.Properties}: [Name = string])", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -548,7 +512,7 @@ public async Task TestAttributeWithArgumentsAndNamedParameters1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) + new($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -575,7 +539,7 @@ public async Task TestAttributeWithArgumentsAndNamedParameters2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "BarParameter", currentParameterIndex: 1) + new($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, "BarParameter", currentParameterIndex: 1) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -602,7 +566,7 @@ public async Task TestAttributeWithArgumentsAndNamedParameters3() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, string.Empty, currentParameterIndex: 2) + new($"SomethingAttribute([int goo = 0], [string bar = null], {FeaturesResources.Properties}: [fieldbar = string], [fieldfoo = int])", string.Empty, string.Empty, currentParameterIndex: 2) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -629,7 +593,7 @@ public async Task TestAttributeWithOptionalArgumentAndNamedParameterWithSameName { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) + new($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, "GooParameter", currentParameterIndex: 0) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -654,7 +618,7 @@ public async Task TestAttributeWithOptionalArgumentAndNamedParameterWithSameName { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 1) + new($"SomethingAttribute([int goo = 0], {FeaturesResources.Properties}: [goo = int])", string.Empty, string.Empty, currentParameterIndex: 1) }; // TODO: Bug 12319: Enable tests for script when this is fixed. @@ -682,7 +646,7 @@ public async Task TestInvocationOnTriggerParens() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 0) + new("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -705,7 +669,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 1) + new("SomethingAttribute(int someParameter, bool somethingElse)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -776,7 +740,7 @@ public MyAttribute(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -809,7 +773,7 @@ public MyAttribute(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -842,7 +806,7 @@ public MyAttribute(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -889,13 +853,13 @@ public MyAttribute(int x, int y) var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("MyAttribute(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) + new("MyAttribute(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("MyAttribute(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -997,7 +961,6 @@ public Task InvokedWithNoToken() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1081535")] public async Task TestInvocationWithBadParameterList() { - var expectedOrderedItems = new List(); await TestAsync(""" class SomethingAttribute : System.Attribute { @@ -1007,6 +970,6 @@ class SomethingAttribute : System.Attribute class D { } - """, expectedOrderedItems); + """, []); } } diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs index bff5937f6bf..9a458567c7d 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ConstructorInitializerSignatureHelpProviderTests.cs @@ -26,11 +26,6 @@ internal override Type GetSignatureHelpProviderType() [Fact] public async Task TestInvocationWithoutParameters() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("BaseClass()", string.Empty, null, currentParameterIndex: 0) - }; - await TestAsync(""" class BaseClass { @@ -42,17 +37,13 @@ class Derived : BaseClass public Derived() [|: base($$|]) { } } - """, expectedOrderedItems); + """, + [new("BaseClass()", string.Empty, null, currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithoutParametersMethodXmlComments() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("BaseClass()", "Summary for BaseClass", null, currentParameterIndex: 0) - }; - await TestAsync(""" class BaseClass { @@ -65,17 +56,13 @@ class Derived : BaseClass public Derived() [|: base($$|]) { } } - """, expectedOrderedItems); + """, + [new("BaseClass()", "Summary for BaseClass", null, currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithParametersOn1() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("BaseClass(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - await TestAsync(""" class BaseClass { @@ -87,17 +74,13 @@ class Derived : BaseClass public Derived() [|: base($$2, 3|]) { } } - """, expectedOrderedItems); + """, + [new("BaseClass(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithParametersXmlCommentsOn1() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("BaseClass(int a, int b)", "Summary for BaseClass", "Param a", currentParameterIndex: 0) - }; - await TestAsync(""" class BaseClass { @@ -112,17 +95,13 @@ class Derived : BaseClass public Derived() [|: base($$2, 3|]) { } } - """, expectedOrderedItems); + """, + [new("BaseClass(int a, int b)", "Summary for BaseClass", "Param a", currentParameterIndex: 0)]); } [Fact] public async Task TestInvocationWithParametersOn2() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1) - }; - await TestAsync(""" class BaseClass { @@ -138,17 +117,13 @@ class Derived : BaseClass public Derived() [|: base(2, $$3|]) { } } - """, expectedOrderedItems); + """, + [new("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1)]); } [Fact] public async Task TestInvocationWithParametersXmlComentsOn2() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1) - }; - await TestAsync(""" class BaseClass { @@ -163,86 +138,65 @@ class Derived : BaseClass public Derived() [|: base(2, $$3|]) { } } - """, expectedOrderedItems); + """, + [new("BaseClass(int a, int b)", "Summary for BaseClass", "Param b", currentParameterIndex: 1)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2579")] public async Task TestThisInvocation() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - await TestAsync(""" class Goo { public Goo(int a, int b) { } public Goo() [|: this(2, $$3|]) { } } - """, expectedOrderedItems); + """, + [new("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2579")] public async Task TestThisInvocationWithNonEmptyArgumentList() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), - }; - await TestAsync(""" class Foo { public Foo(int a, int b) [|: this($$|]) { } public Foo() { } } - """, expectedOrderedItems); + """, + [new("Foo()", string.Empty, null, currentParameterIndex: 0)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2579")] public async Task TestInvocationWithoutClosingParen() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - await TestAsync(""" class Goo { public Goo(int a, int b) { } public Goo() [|: this(2, $$ |]} - """, expectedOrderedItems); + """, + [new("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2579")] public async Task TestThisInvocationWithoutClosingParenWithNonEmptyArgumentList() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), - }; - await TestAsync(""" class Foo { public Foo() { } public Foo(int a, int b) [|: this($$ |]} - """, expectedOrderedItems); + """, + [new("Foo()", string.Empty, null, currentParameterIndex: 0)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25830")] public async Task PickCorrectOverload_PickInt() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0), - }; - await TestAsync(""" class D { @@ -252,18 +206,14 @@ class D D(string i) => throw null; D(int i) => throw null; } - """, expectedOrderedItems); + """, [ + new("D(int i)", currentParameterIndex: 0, isSelected: true), + new("D(string i)", currentParameterIndex: 0)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25830")] public async Task PickCorrectOverload_PickString() { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0, isSelected: true), - }; - await TestAsync(""" class D { @@ -273,7 +223,9 @@ class D D(string i) => throw null; D(int i) => throw null; } - """, expectedOrderedItems); + """, [ + new("D(int i)", currentParameterIndex: 0), + new("D(string i)", currentParameterIndex: 0, isSelected: true)]); } #endregion @@ -299,7 +251,7 @@ public async Task TestInvocationOnTriggerParens() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int a)", string.Empty, string.Empty, currentParameterIndex: 0) + new("Goo(int a)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -316,7 +268,7 @@ public async Task TestInvocationOnTriggerParensWithNonEmptyArgumentList() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), + new("Foo()", string.Empty, null, currentParameterIndex: 0), }; await TestAsync(""" @@ -333,7 +285,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) + new("Goo(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -350,7 +302,7 @@ public async Task TestInvocationOnTriggerCommaWithNonEmptyArgumentList() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Foo()", string.Empty, null, currentParameterIndex: 0), + new("Foo()", string.Empty, null, currentParameterIndex: 0), }; await TestAsync(""" @@ -407,7 +359,7 @@ public BaseClass(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -438,7 +390,7 @@ public BaseClass(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -469,7 +421,7 @@ public BaseClass(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -513,13 +465,13 @@ public BaseClass(int x, int y) """; var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("BaseClass(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) + new("BaseClass(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("BaseClass(int x, int y)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -647,7 +599,7 @@ public async Task TypingTupleDoesNotDismiss1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -665,7 +617,7 @@ public async Task TypingTupleDoesNotDismiss2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -682,7 +634,7 @@ public async Task TypingTupleDoesNotDismiss3() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -700,7 +652,7 @@ public async Task TypingTupleDoesNotDismiss4() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("D(object o)", currentParameterIndex: 0) + new("D(object o)", currentParameterIndex: 0) }; await TestAsync(""" @@ -725,7 +677,7 @@ public async Task PickCorrectOverload_NamesAndEmptyPositions(string arguments, i { var expectedOrderedItems = new List { - new SignatureHelpTestItem("Program(int i1, int i2, int i3)", currentParameterIndex: expectedParameterIndex, isSelected: true), + new("Program(int i1, int i2, int i3)", currentParameterIndex: expectedParameterIndex, isSelected: true), }; await TestAsync($$""" @@ -759,8 +711,8 @@ public async Task PickCorrectOverload_Incomplete(string arguments, int expectedP var index = 0; var expectedOrderedItems = new List { - new SignatureHelpTestItem("Program(int i, string s)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), - new SignatureHelpTestItem("Program(string s, string s2)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), + new("Program(int i, string s)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), + new("Program(string s, string s2)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), }; await TestAsync($$""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs index 7ab810931f8..460ee6ad0da 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/ElementAccessExpressionSignatureHelpProviderTests.cs @@ -28,7 +28,7 @@ public async Task TestInvocationWithParametersOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -57,7 +57,7 @@ public async Task TestInvocationWithParametersOn1_WithRefReturn() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("ref int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("ref int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -80,7 +80,7 @@ public async Task TestInvocationWithParametersOn1_WithRefReadonlyReturn() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("ref readonly int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("ref readonly int C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -103,7 +103,7 @@ public async Task TestInvocationOnExpression() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -132,7 +132,7 @@ public async Task TestInvocationWithParametersXmlCommentsOn1() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", "Summary for this.", "Param a", currentParameterIndex: 0) + new("string C[int a]", "Summary for this.", "Param a", currentParameterIndex: 0) }; await TestAsync(""" @@ -165,7 +165,7 @@ public async Task TestInvocationWithParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) + new("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -194,7 +194,7 @@ public async Task TestInvocationWithParametersXmlComentsOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", "Summary for this.", "Param b", currentParameterIndex: 1) + new("string C[int a, bool b]", "Summary for this.", "Param b", currentParameterIndex: 1) }; await TestAsync(""" @@ -228,7 +228,7 @@ public async Task TestInvocationWithoutClosingBracketWithParameters() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -257,7 +257,7 @@ public async Task TestInvocationWithoutClosingBracketWithParametersOn2() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) + new("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -316,7 +316,7 @@ public async Task TestInvocationOnTriggerBracket() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -345,7 +345,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) + new("string C[int a, bool b]", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" @@ -433,7 +433,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -470,7 +470,7 @@ public int this[int x] """; var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -507,7 +507,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -560,13 +560,13 @@ public int this[double d] var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0), + new("int Goo[double d]", string.Empty, string.Empty, currentParameterIndex: 0), + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0), }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -603,7 +603,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -640,7 +640,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -678,7 +678,7 @@ public int this[int x] """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int Goo[int x]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -739,12 +739,12 @@ End Class var metadataItems = new List { - new SignatureHelpTestItem("string CCC.IndexProp[int p1]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string CCC.IndexProp[int p1]", string.Empty, string.Empty, currentParameterIndex: 0) }; var projectReferenceItems = new List { - new SignatureHelpTestItem("string CCC.IndexProp[int p1]", "An index property from VB", "p1 is an integer index", currentParameterIndex: 0) + new("string CCC.IndexProp[int p1]", "An index property from VB", "p1 is an integer index", currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -856,7 +856,7 @@ public async Task TestInvocation() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) + new("string C[int a]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -885,7 +885,7 @@ public async Task ConditionalIndexer() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("int P[int z]", string.Empty, string.Empty, currentParameterIndex: 0) + new("int P[int z]", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameFullyWrittenSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameFullyWrittenSignatureHelpProviderTests.cs new file mode 100644 index 00000000000..dbef426582d --- /dev/null +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameFullyWrittenSignatureHelpProviderTests.cs @@ -0,0 +1,874 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.SignatureHelp; +using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; + +[Trait(Traits.Feature, Traits.Features.SignatureHelp)] +public sealed class GenericNameFullyWrittenSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests +{ + internal override Type GetSignatureHelpProviderType() + => typeof(GenericNameFullyWrittenSignatureHelpProvider); + + #region "Declaring generic type objects" + + [Fact] + public async Task NestedGenericTerminated() + { + await TestAsync(""" + class G { }; + + class C + { + void Goo() + { + G$$> + } + } + """, + [new("G", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWith1ParameterTerminated() + { + await TestAsync(""" + class G { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWith2ParametersOn1() + { + await TestAsync(""" + class G { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWith2ParametersOn2() + { + await TestAsync(""" + class G { }; + + class C + { + void Goo() + { + [|G + } + } + """, + [new("G", string.Empty, string.Empty, currentParameterIndex: 1)]); + } + + [Fact] + public async Task DeclaringGenericTypeWith2ParametersOn1XmlDoc() + { + await TestAsync(""" + /// + /// Summary for G + /// + /// TypeParamS. Also see + /// TypeParamT + class G { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G", + "Summary for G", + "TypeParamS. Also see T", + currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWith2ParametersOn2XmlDoc() + { + await TestAsync(""" + /// + /// Summary for G + /// + /// TypeParamS + /// TypeParamT. Also see + class G { }; + + class C + { + void Goo() + { + [|G + } + } + """, + [new("G", "Summary for G", "TypeParamT. Also see S", currentParameterIndex: 1)]); + } + + #endregion + + #region "Constraints on generic types" + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsStruct() + { + await TestAsync(""" + class G where S : struct + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : struct", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsClass() + { + await TestAsync(""" + class G where S : class + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : class", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsNew() + { + await TestAsync(""" + class G where S : new() + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : new()", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsBase() + { + await TestAsync(""" + class Base { } + + class G where S : Base + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsBaseGenericWithGeneric() + { + await TestAsync(""" + class Base { } + + class G where S : Base + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsBaseGenericWithNonGeneric() + { + await TestAsync(""" + class Base { } + + class G where S : Base + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsBaseGenericNested() + { + await TestAsync(""" + class Base { } + + class G where S : Base> + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : Base>", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsDeriveFromAnotherGenericParameter() + { + await TestAsync(""" + class G where S : T + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : T", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsMixed1() + { + await TestAsync(""" + /// + /// Summary1 + /// + /// SummaryS + /// SummaryT + class G + where S : Base, new() + where T : class, S, IGoo, new() + { }; + + internal interface IGoo { } + + internal class Base { } + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : Base, new()", "Summary1", "SummaryS", currentParameterIndex: 0)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsMixed2() + { + await TestAsync(""" + /// + /// Summary1 + /// + /// SummaryS + /// SummaryT + class G + where S : Base, new() + where T : class, S, IGoo, new() + { }; + + internal interface IGoo { } + + internal class Base { } + + class C + { + void Goo() + { + [|G + } + } + """, + [new("G where T : class, S, IGoo, new()", "Summary1", "SummaryT", currentParameterIndex: 1)]); + } + + [Fact] + public async Task DeclaringGenericTypeWithConstraintsAllowRefStruct() + { + await TestAsync(""" + class G where S : allows ref struct + { }; + + class C + { + void Goo() + { + [|G<$$|]> + } + } + """, + [new("G where S : allows ref struct", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + #endregion + + #region "Generic member invocation" + + [Fact] + public async Task InvokingGenericMethodWith1ParameterTerminated() + { + await TestAsync(""" + class C + { + void Goo() { } + + void Bar() + { + [|Goo<$$|]> + } + } + """, + [new("void C.Goo()", string.Empty, string.Empty, currentParameterIndex: 0)]); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] + public async Task InvokingGenericMethodWith2ParametersOn1() + { + await TestAsync(""" + class C + { + /// + /// Method summary + /// + /// type param S. see + /// type param T. + /// parameter s + /// parameter t + void Goo(S s, T t) { } + + void Bar() + { + [|Goo<$$|]> + } + } + """, + [new("void C.Goo(S s, T t)", + "Method summary", "type param S. see T", currentParameterIndex: 0)]); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] + public async Task InvokingGenericMethodWith2ParametersOn2() + { + await TestAsync(""" + class C + { + void Goo(S s, T t) { } + + void Bar() + { + [|Goo + } + } + """, + [new("void C.Goo(S s, T t)", string.Empty, string.Empty, currentParameterIndex: 1)]); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] + public async Task InvokingGenericMethodWith2ParametersOn1XmlDoc() + { + await TestAsync(""" + class C + { + /// + /// SummaryForGoo + /// + /// SummaryForS + /// SummaryForT + void Goo(S s, T t) { } + + void Bar() + { + [|Goo<$$|]> + } + } + """, + [new("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForS", currentParameterIndex: 0)]); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] + public async Task InvokingGenericMethodWith2ParametersOn2XmlDoc() + { + await TestAsync(""" + class C + { + /// + /// SummaryForGoo + /// + /// SummaryForS + /// SummaryForT + void Goo(S s, T t) { } + + void Bar() + { + [|Goo + } + } + """, + [new("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForT", currentParameterIndex: 1)]); + } + + [Fact] + public async Task CallingGenericExtensionMethod() + { + // TODO: Enable the script case when we have support for extension methods in scripts + await TestAsync(""" + class G + { }; + + class C + { + void Bar() + { + G g = null; + g.[|Goo<$$|]> + } + } + + static class GooClass + { + public static void Goo(this G g) { } + } + """, + [new($"({CSharpFeaturesResources.extension}) void G.Goo()", string.Empty, string.Empty, currentParameterIndex: 0)], + usePreviousCharAsTrigger: false, sourceCodeKind: SourceCodeKind.Regular); + } + + #endregion + + #region "Constraints on generic methods" + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] + public async Task InvokingGenericMethodWithConstraintsMixed1() + { + await TestAsync(""" + class Base { } + interface IGoo { } + + class C + { + /// + /// GooSummary + /// + /// ParamS + /// ParamT + S Goo(S s, T t) + where S : Base, new() + where T : class, S, IGoo, new() + { return null; } + + void Bar() + { + [|Goo<$$|]> + } + } + """, [new("S C.Goo(S s, T t) where S : Base, new()", "GooSummary", "ParamS", currentParameterIndex: 0)]); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] + public async Task InvokingGenericMethodWithConstraintsMixed2() + { + await TestAsync(""" + class Base { } + interface IGoo { } + + class C + { + /// + /// GooSummary + /// + /// ParamS + /// ParamT + S Goo(S s, T t) + where S : Base, new() + where T : class, S, IGoo, new() + { return null; } + + void Bar() + { + [|Goo + } + } + """, [new("S C.Goo(S s, T t) where T : class, S, IGoo, new()", "GooSummary", "ParamT", currentParameterIndex: 1)]); + } + + [Fact] + public Task TestUnmanagedConstraint() + => TestAsync(""" + class C + { + /// + /// summary headline + /// + /// T documentation + void M(T arg) where T : unmanaged + { + } + + void Bar() + { + [|M<$$|]> + } + } + """, + [new SignatureHelpTestItem("void C.M(T arg) where T : unmanaged", "summary headline", "T documentation", currentParameterIndex: 0)]); + + #endregion + + #region "Trigger tests" + + [Fact] + public void TestTriggerCharacters() + { + char[] expectedCharacters = [',', '<']; + char[] unexpectedCharacters = [' ', '[', '(']; + + VerifyTriggerCharacters(expectedCharacters, unexpectedCharacters); + } + + [Fact] + public async Task FieldUnavailableInOneLinkedFile() + { + var expectedDescription = new SignatureHelpTestItem($""" + D + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, currentParameterIndex: 0); + await VerifyItemWithReferenceWorkerAsync(""" + + + + { + } + #endif + void goo() + { + var x = new D<$$ + } + } + ]]> + + + + + + + """, [expectedDescription], false); + } + + [Fact] + public async Task ExcludeFilesWithInactiveRegions() + { + var expectedDescription = new SignatureHelpTestItem($""" + D + + {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} + {string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)} + + {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} + """, currentParameterIndex: 0); + await VerifyItemWithReferenceWorkerAsync(""" + + + + { + } + #endif + + #if BAR + void goo() + { + var x = new D<$$ + } + #endif + } + ]]> + + + + + + + + + + """, [expectedDescription], false); + } + + #endregion + + #region "EditorBrowsable tests" + + [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] + public async Task EditorBrowsable_GenericType_BrowsableAlways() + { + var markup = """ + class Program + { + void M() + { + var c = new C<$$ + } + } + """; + + var referencedCode = """ + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always)] + public class C + { + } + """; + + var expectedOrderedItems = new List + { + new("C", string.Empty, string.Empty, currentParameterIndex: 0) + }; + + await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: expectedOrderedItems, + expectedOrderedItemsSameSolution: expectedOrderedItems, + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp); + } + + [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] + public async Task EditorBrowsable_GenericType_BrowsableNever() + { + var markup = """ + class Program + { + void M() + { + var c = new C<$$ + } + } + """; + + var referencedCode = """ + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + public class C + { + } + """; + + var expectedOrderedItems = new List + { + new("C", string.Empty, string.Empty, currentParameterIndex: 0) + }; + + await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: [], + expectedOrderedItemsSameSolution: expectedOrderedItems, + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp); + } + + [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] + public async Task EditorBrowsable_GenericType_BrowsableAdvanced() + { + var markup = """ + class Program + { + void M() + { + var c = new C<$$ + } + } + """; + + var referencedCode = """ + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] + public class C + { + } + """; + + var expectedOrderedItems = new List + { + new("C", string.Empty, string.Empty, currentParameterIndex: 0) + }; + + await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: [], + expectedOrderedItemsSameSolution: expectedOrderedItems, + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp, + hideAdvancedMembers: true); + + await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: expectedOrderedItems, + expectedOrderedItemsSameSolution: expectedOrderedItems, + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp, + hideAdvancedMembers: false); + } + #endregion + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1083601")] + public async Task DeclaringGenericTypeWithBadTypeArgumentList() + { + await TestAsync(""" + class G { }; + + class C + { + void Goo() + { + G{$$> + } + } + """, []); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50114")] + public async Task DeclaringGenericTypeWithDocCommentList() + { + await TestAsync(""" + /// + /// List: + /// + /// + /// + /// Item 1. + /// + /// + /// + /// + class G { }; + + class C + { + void Goo() + { + [|G + } + } + """, + [new("G", """ + List: + + Item 1. + """, + classificationTypeNames: ImmutableArray.Create( + ClassificationTypeNames.Text, + ClassificationTypeNames.WhiteSpace, + ClassificationTypeNames.WhiteSpace, + ClassificationTypeNames.WhiteSpace, + ClassificationTypeNames.Text, + ClassificationTypeNames.WhiteSpace))]); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80233")] + public Task TestModernGenericExtensionMethodOnGenericType() + => TestAsync(""" + public class My + { + } + + public static class MyExtensions + { + extension(My self) + { + public void GenericMethod() + { + } + } + } + + class User + { + static void Main() + { + My target = new(); + target.GenericMethod<$$>(); + } + } + """, + [new("void MyExtensions.extension(My).GenericMethod()")], + sourceCodeKind: SourceCodeKind.Regular); +} diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs index 03868077884..15833b8b3f3 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProviderTests.cs @@ -27,7 +27,7 @@ public async Task NestedGenericUnterminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -48,7 +48,7 @@ public async Task NestedGenericUnterminatedWithAmbiguousShift() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -71,7 +71,7 @@ public async Task NestedGenericUnterminatedWithAmbiguousUnsignedShift() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -94,7 +94,7 @@ public async Task DeclaringGenericTypeWith1ParameterUnterminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -115,7 +115,7 @@ public async Task CallingGenericAsyncMethod() { var expectedOrderedItems = new List { - new SignatureHelpTestItem($"({CSharpFeaturesResources.awaitable}) Task Program.Goo()", methodDocumentation: string.Empty, string.Empty, currentParameterIndex: 0) + new($"({CSharpFeaturesResources.awaitable}) Task Program.Goo()", methodDocumentation: string.Empty, string.Empty, currentParameterIndex: 0) }; // TODO: Enable the script case when we have support for extension methods in scripts @@ -159,7 +159,7 @@ public void Goo(T x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -194,7 +194,7 @@ public void Goo(T x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -229,7 +229,7 @@ public void Goo(T x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -276,13 +276,13 @@ public void Goo(T x, U y) """; var expectedOrderedItemsMetadataReference = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0) }; var expectedOrderedItemsSameSolution = new List { - new SignatureHelpTestItem("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("void C.Goo(T x, U y)", string.Empty, string.Empty, currentParameterIndex: 0) + new("void C.Goo(T x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("void C.Goo(T x, U y)", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, @@ -298,8 +298,8 @@ public async Task GenericExtensionMethod() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void IGoo.Bar()", currentParameterIndex: 0), - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void IGoo.Bar()", currentParameterIndex: 0), + new("void IGoo.Bar()", currentParameterIndex: 0), + new($"({CSharpFeaturesResources.extension}) void IGoo.Bar()", currentParameterIndex: 0), }; // Extension methods are supported in Interactive/Script (yet). @@ -330,7 +330,7 @@ public async Task InvokingGenericMethodWith1ParameterUnterminated() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("void C.Goo()", + new("void C.Goo()", "Method Goo", "Method type parameter", currentParameterIndex: 0) }; @@ -356,7 +356,7 @@ public async Task TestInvocationOnTriggerBracket() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) + new("G", string.Empty, string.Empty, currentParameterIndex: 0) }; await TestAsync(""" @@ -377,7 +377,7 @@ public async Task TestInvocationOnTriggerComma() { var expectedOrderedItems = new List { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 1) + new("G", string.Empty, string.Empty, currentParameterIndex: 1) }; await TestAsync(""" diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs index 62adec90b36..3ed04d2188b 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/GenericNameSignatureHelpProviderTests.cs @@ -1,955 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +PLEASE READ -#nullable disable - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.SignatureHelp; -using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; -using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; - -[Trait(Traits.Feature, Traits.Features.SignatureHelp)] -public sealed class GenericNameSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests -{ - internal override Type GetSignatureHelpProviderType() - => typeof(GenericNameSignatureHelpProvider); - - #region "Declaring generic type objects" - - [Fact] - public async Task NestedGenericTerminated() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G { }; - - class C - { - void Goo() - { - G$$> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWith1ParameterTerminated() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWith2ParametersOn1() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWith2ParametersOn2() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", string.Empty, string.Empty, currentParameterIndex: 1) - }; - - await TestAsync(""" - class G { }; - - class C - { - void Goo() - { - [|G - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWith2ParametersOn1XmlDoc() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", - "Summary for G", - "TypeParamS. Also see T", - currentParameterIndex: 0) - }; - - await TestAsync(""" - /// - /// Summary for G - /// - /// TypeParamS. Also see - /// TypeParamT - class G { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWith2ParametersOn2XmlDoc() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", "Summary for G", "TypeParamT. Also see S", currentParameterIndex: 1) - }; - - await TestAsync(""" - /// - /// Summary for G - /// - /// TypeParamS - /// TypeParamT. Also see - class G { }; - - class C - { - void Goo() - { - [|G - } - } - """, expectedOrderedItems); - } - - #endregion - - #region "Constraints on generic types" - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsStruct() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : struct", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G where S : struct - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsClass() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : class", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G where S : class - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsNew() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : new()", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G where S : new() - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsBase() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class Base { } - - class G where S : Base - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsBaseGenericWithGeneric() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class Base { } - - class G where S : Base - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsBaseGenericWithNonGeneric() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : Base", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class Base { } - - class G where S : Base - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsBaseGenericNested() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : Base>", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class Base { } - - class G where S : Base> - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsDeriveFromAnotherGenericParameter() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : T", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G where S : T - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsMixed1() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : Base, new()", "Summary1", "SummaryS", currentParameterIndex: 0) - }; - - await TestAsync(""" - /// - /// Summary1 - /// - /// SummaryS - /// SummaryT - class G - where S : Base, new() - where T : class, S, IGoo, new() - { }; - - internal interface IGoo { } - - internal class Base { } - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsMixed2() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where T : class, S, IGoo, new()", "Summary1", "SummaryT", currentParameterIndex: 1) - }; - - await TestAsync(""" - /// - /// Summary1 - /// - /// SummaryS - /// SummaryT - class G - where S : Base, new() - where T : class, S, IGoo, new() - { }; - - internal interface IGoo { } - - internal class Base { } - - class C - { - void Goo() - { - [|G - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task DeclaringGenericTypeWithConstraintsAllowRefStruct() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G where S : allows ref struct", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class G where S : allows ref struct - { }; - - class C - { - void Goo() - { - [|G<$$|]> - } - } - """, expectedOrderedItems); - } - - #endregion - - #region "Generic member invocation" - - [Fact] - public async Task InvokingGenericMethodWith1ParameterTerminated() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void C.Goo()", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(""" - class C - { - void Goo() { } - - void Bar() - { - [|Goo<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] - public async Task InvokingGenericMethodWith2ParametersOn1() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void C.Goo(S s, T t)", - "Method summary", "type param S. see T", currentParameterIndex: 0) - }; - - await TestAsync(""" - class C - { - /// - /// Method summary - /// - /// type param S. see - /// type param T. - /// parameter s - /// parameter t - void Goo(S s, T t) { } - - void Bar() - { - [|Goo<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] - public async Task InvokingGenericMethodWith2ParametersOn2() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void C.Goo(S s, T t)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - - await TestAsync(""" - class C - { - void Goo(S s, T t) { } - - void Bar() - { - [|Goo - } - } - """, expectedOrderedItems); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] - public async Task InvokingGenericMethodWith2ParametersOn1XmlDoc() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForS", currentParameterIndex: 0) - }; - - await TestAsync(""" - class C - { - /// - /// SummaryForGoo - /// - /// SummaryForS - /// SummaryForT - void Goo(S s, T t) { } - - void Bar() - { - [|Goo<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] - public async Task InvokingGenericMethodWith2ParametersOn2XmlDoc() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void C.Goo(S s, T t)", "SummaryForGoo", "SummaryForT", currentParameterIndex: 1) - }; - - await TestAsync(""" - class C - { - /// - /// SummaryForGoo - /// - /// SummaryForS - /// SummaryForT - void Goo(S s, T t) { } - - void Bar() - { - [|Goo - } - } - """, expectedOrderedItems); - } - - [Fact] - public async Task CallingGenericExtensionMethod() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void G.Goo()", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - // TODO: Enable the script case when we have support for extension methods in scripts - await TestAsync(""" - class G - { }; - - class C - { - void Bar() - { - G g = null; - g.[|Goo<$$|]> - } - } - - static class GooClass - { - public static void Goo(this G g) { } - } - """, expectedOrderedItems, usePreviousCharAsTrigger: false, sourceCodeKind: Microsoft.CodeAnalysis.SourceCodeKind.Regular); - } - - #endregion - - #region "Constraints on generic methods" - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] - public async Task InvokingGenericMethodWithConstraintsMixed1() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("S C.Goo(S s, T t) where S : Base, new()", "GooSummary", "ParamS", currentParameterIndex: 0) - }; - - await TestAsync(""" - class Base { } - interface IGoo { } - - class C - { - /// - /// GooSummary - /// - /// ParamS - /// ParamT - S Goo(S s, T t) - where S : Base, new() - where T : class, S, IGoo, new() - { return null; } - - void Bar() - { - [|Goo<$$|]> - } - } - """, expectedOrderedItems); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544091")] - public async Task InvokingGenericMethodWithConstraintsMixed2() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("S C.Goo(S s, T t) where T : class, S, IGoo, new()", "GooSummary", "ParamT", currentParameterIndex: 1) - }; - - await TestAsync(""" - class Base { } - interface IGoo { } - - class C - { - /// - /// GooSummary - /// - /// ParamS - /// ParamT - S Goo(S s, T t) - where S : Base, new() - where T : class, S, IGoo, new() - { return null; } - - void Bar() - { - [|Goo - } - } - """, expectedOrderedItems); - } - - [Fact] - public Task TestUnmanagedConstraint() - => TestAsync(""" - class C - { - /// - /// summary headline - /// - /// T documentation - void M(T arg) where T : unmanaged - { - } - - void Bar() - { - [|M<$$|]> - } - } - """, - [ - new SignatureHelpTestItem("void C.M(T arg) where T : unmanaged", "summary headline", "T documentation", currentParameterIndex: 0) - ]); - - #endregion - - #region "Trigger tests" - - [Fact] - public void TestTriggerCharacters() - { - char[] expectedCharacters = [',', '<']; - char[] unexpectedCharacters = [' ', '[', '(']; - - VerifyTriggerCharacters(expectedCharacters, unexpectedCharacters); - } - - [Fact] - public async Task FieldUnavailableInOneLinkedFile() - { - var expectedDescription = new SignatureHelpTestItem($""" - D - - {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} - {string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)} - - {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} - """, currentParameterIndex: 0); - await VerifyItemWithReferenceWorkerAsync(""" - - - - { - } - #endif - void goo() - { - var x = new D<$$ - } - } - ]]> - - - - - - - """, [expectedDescription], false); - } - - [Fact] - public async Task ExcludeFilesWithInactiveRegions() - { - var expectedDescription = new SignatureHelpTestItem($""" - D - - {string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)} - {string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)} - - {FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts} - """, currentParameterIndex: 0); - await VerifyItemWithReferenceWorkerAsync(""" - - - - { - } - #endif - - #if BAR - void goo() - { - var x = new D<$$ - } - #endif - } - ]]> - - - - - - - - - - """, [expectedDescription], false); - } - - #endregion - - #region "EditorBrowsable tests" - - [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] - public async Task EditorBrowsable_GenericType_BrowsableAlways() - { - var markup = """ - class Program - { - void M() - { - var c = new C<$$ - } - } - """; - - var referencedCode = """ - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always)] - public class C - { - } - """; - - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: expectedOrderedItems, - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp); - } - - [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] - public async Task EditorBrowsable_GenericType_BrowsableNever() - { - var markup = """ - class Program - { - void M() - { - var c = new C<$$ - } - } - """; - - var referencedCode = """ - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public class C - { - } - """; - - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: [], - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp); - } - - [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] - public async Task EditorBrowsable_GenericType_BrowsableAdvanced() - { - var markup = """ - class Program - { - void M() - { - var c = new C<$$ - } - } - """; - - var referencedCode = """ - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] - public class C - { - } - """; - - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: [], - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp, - hideAdvancedMembers: true); - - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: expectedOrderedItems, - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp, - hideAdvancedMembers: false); - } - #endregion - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1083601")] - public async Task DeclaringGenericTypeWithBadTypeArgumentList() - { - var expectedOrderedItems = new List(); - await TestAsync(""" - class G { }; - - class C - { - void Goo() - { - G{$$> - } - } - """, expectedOrderedItems); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50114")] - public async Task DeclaringGenericTypeWithDocCommentList() - { - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("G", """ - List: - - Item 1. - """, - classificationTypeNames: ImmutableArray.Create( - ClassificationTypeNames.Text, - ClassificationTypeNames.WhiteSpace, - ClassificationTypeNames.WhiteSpace, - ClassificationTypeNames.WhiteSpace, - ClassificationTypeNames.Text, - ClassificationTypeNames.WhiteSpace)) - }; - - await TestAsync(""" - /// - /// List: - /// - /// - /// - /// Item 1. - /// - /// - /// - /// - class G { }; - - class C - { - void Goo() - { - [|G - } - } - """, expectedOrderedItems); - } -} +Please remove this file during conflict resolution in your PR. +This file has been reverted (removed) in the source repository but the PR branch +does not have the file yet as it's based on an older commit. This means the file is +not getting removed in the PR due to the other conflicts. \ No newline at end of file diff --git a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs index 7b4d9958c9a..4b6b6f02633 100644 --- a/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/CSharpTest/SignatureHelp/InvocationExpressionSignatureHelpProviderTests.cs @@ -1101,8 +1101,8 @@ public void Goo(int x) """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("void B.Goo()", string.Empty, null, currentParameterIndex: 0), - new SignatureHelpTestItem("void D.Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("void B.Goo()", string.Empty, null, currentParameterIndex: 0), + new("void D.Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0), }; await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, diff --git a/src/roslyn/src/EditorFeatures/Core/Copilot/RoslynProposalAdjusterProvider.cs b/src/roslyn/src/EditorFeatures/Core/Copilot/RoslynProposalAdjusterProvider.cs index f82a7549749..c1e44592db3 100644 --- a/src/roslyn/src/EditorFeatures/Core/Copilot/RoslynProposalAdjusterProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/Copilot/RoslynProposalAdjusterProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Globalization; using System.Linq; @@ -14,6 +15,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Language.Proposals; @@ -37,9 +39,13 @@ public override Task AdjustProposalBeforeDisplayAsync(ProposalBase public override Task AdjustProposalAfterAcceptAsync(ProposalBase proposal, string providerName, CancellationToken cancellationToken) => AdjustProposalAsync(proposal, providerName, before: false, cancellationToken); - private static void SetDefaultTelemetryProperties(Dictionary map, string providerName, bool before, TimeSpan elapsedTime) + private static void SetDefaultTelemetryProperties( + Dictionary map, string? proposalId, string providerName, bool before, TimeSpan elapsedTime) { // Common properties that all adjustments will log. + if (proposalId != null) + map["ProposalId"] = proposalId; + map["ProviderName"] = providerName; map["AdjustProposalBeforeDisplay"] = before; map["ComputationTime"] = elapsedTime.TotalMilliseconds.ToString("G17", CultureInfo.InvariantCulture); @@ -58,11 +64,11 @@ private async Task AdjustProposalAsync( // If we can't find a solution, then we can't adjust the proposal. Log telemetry and return the original proposal. Logger.LogBlock(FunctionId.Copilot_AdjustProposal, KeyValueLogMessage.Create(static (d, args) => { - var (providerName, before, failureReason, elapsedTime) = args; - SetDefaultTelemetryProperties(d, providerName, before, elapsedTime); + var (proposal, providerName, before, failureReason, elapsedTime) = args; + SetDefaultTelemetryProperties(d, proposal.ProposalId, providerName, before, elapsedTime); d["SolutionAcquisitionFailure"] = failureReason; }, - args: (providerName, before, failureReason, stopwatch.Elapsed)), + args: (proposal, providerName, before, failureReason, stopwatch.Elapsed)), cancellationToken).Dispose(); return proposal; @@ -82,13 +88,13 @@ private async Task AdjustProposalAsync( { try { - var newProposal = await AdjustProposalAsync( + var (newProposal, adjustmentKinds) = await AdjustProposalAsync( solution, proposal, cancellationToken).ConfigureAwait(false); // Report telemetry if we were or were not able to adjust the proposal. Logger.LogBlock(FunctionId.Copilot_AdjustProposal, KeyValueLogMessage.Create(static (d, args) => { - var (providerName, before, proposal, newProposal, elapsedTime) = args; + var (providerName, before, proposal, newProposal, adjustmentKinds, elapsedTime) = args; // If we (roslyn) were able to come up with *any* edits we wanted to adjust the proposal with or not. var adjustmentsProposed = newProposal != null; @@ -99,7 +105,7 @@ private async Task AdjustProposalAsync( // for example, if it thinks they would interfere with the caret, or if edits intersect other edits. var adjustmentsAccepted = newProposal != proposal; - SetDefaultTelemetryProperties(d, providerName, before, elapsedTime); + SetDefaultTelemetryProperties(d, proposal.ProposalId, providerName, before, elapsedTime); d["AdjustmentsProposed"] = adjustmentsProposed; d["AdjustmentsAccepted"] = adjustmentsAccepted; @@ -107,8 +113,10 @@ private async Task AdjustProposalAsync( // Record how many new edits were made to the proposal. Expectation is that this is commonly only 1, // but we want to see how that potentially changes over time, especially as we add more adjusters. d["AdjustmentsCount"] = newProposal.Edits.Count - proposal.Edits.Count; + if (adjustmentKinds.Length > 0) + d["AdjustmentKinds"] = string.Join(",", adjustmentKinds); }, - args: (providerName, before, proposal, newProposal, stopwatch.Elapsed)), + args: (providerName, before, proposal, newProposal, adjustmentKinds, stopwatch.Elapsed)), cancellationToken).Dispose(); return newProposal ?? proposal; @@ -128,8 +136,8 @@ bool ReportFailureTelemetry(Exception ex) Logger.LogBlock(FunctionId.Copilot_AdjustProposal, KeyValueLogMessage.Create(static (d, args) => { - var (providerName, before, ex, elapsedTime) = args; - SetDefaultTelemetryProperties(d, providerName, before, elapsedTime); + var (proposal, providerName, before, ex, elapsedTime) = args; + SetDefaultTelemetryProperties(d, proposal.ProposalId, providerName, before, elapsedTime); d["AdjustmentsProposed"] = false; if (ex is OperationCanceledException) @@ -142,7 +150,7 @@ bool ReportFailureTelemetry(Exception ex) d["Failed"] = true; } }, - args: (providerName, before, ex, stopwatch.Elapsed)), + args: (proposal, providerName, before, ex, stopwatch.Elapsed)), cancellationToken).Dispose(); return true; @@ -154,15 +162,17 @@ bool ReportFailureTelemetry(Exception ex) /// adjusted proposal based on the edits we tried to make. Note that this does not guarantee that the edits were successfully /// applied to the original edits. The system may reject them based on their own criteria. /// - private async Task AdjustProposalAsync( + private async Task<(ProposalBase? proposal, ImmutableArray adjustmentKinds)> AdjustProposalAsync( Solution solution, ProposalBase proposal, CancellationToken cancellationToken) { // We're potentially making multiple calls to oop here. So keep a session alive to avoid // resyncing the solution and recomputing compilations. using var _1 = await RemoteKeepAliveSession.CreateAsync(solution, cancellationToken).ConfigureAwait(false); using var _2 = PooledObjects.ArrayBuilder.GetInstance(out var finalEdits); + using var _3 = PooledObjects.ArrayBuilder.GetInstance(out var adjustmentKinds); var adjustmentsProposed = false; + var format = false; foreach (var editGroup in proposal.Edits.GroupBy(e => e.Span.Snapshot)) { cancellationToken.ThrowIfCancellationRequested(); @@ -173,9 +183,11 @@ bool ReportFailureTelemetry(Exception ex) // Checked in TryGetAffectedSolution Contract.ThrowIfNull(document); - var proposalAdjusterService = document.Project.Solution.Services.GetRequiredService(); - var proposedEdits = await proposalAdjusterService.TryAdjustProposalAsync( - document, CopilotEditorUtilities.TryGetNormalizedTextChanges(editGroup), cancellationToken).ConfigureAwait(false); + var proposalAdjusterService = document.GetLanguageService(); + var (proposedEdits, formatGroup, changeKinds) = proposalAdjusterService is null + ? default + : await proposalAdjusterService.TryAdjustProposalAsync( + document, CopilotEditorUtilities.TryGetNormalizedTextChanges(editGroup), cancellationToken).ConfigureAwait(false); if (proposedEdits.IsDefault) { @@ -186,6 +198,9 @@ bool ReportFailureTelemetry(Exception ex) { // Changes were made to the proposal. Add the new edits. adjustmentsProposed = true; + format = format || formatGroup; + adjustmentKinds.AddRange(changeKinds); + foreach (var proposedEdit in proposedEdits) { finalEdits.Add(new ProposedEdit( @@ -197,12 +212,20 @@ bool ReportFailureTelemetry(Exception ex) // No adjustments were made. Don't touch anything. if (!adjustmentsProposed) - return null; + return default; // We have some changes we want to to make to the proposal. See if the proposal system allows us merging // those changes in. Note: we should generally always be producing edits that are safe to merge in. However, // as we do not control this code, we cannot guarantee this. Telemetry will let us know how often this happens // and if there's something we need to look into. - return Proposal.TryCreateProposal(proposal, finalEdits); + var result = Proposal.TryCreateProposal(proposal, finalEdits); + if (result is null) + return default; + + if (format && !result.Flags.HasFlag(ProposalFlags.FormatAfterCommit)) + result = new Proposal(result.Description, result.Edits, result.Caret, result.CompletionState, result.Flags | ProposalFlags.FormatAfterCommit, result.CommitAction, result.ProposalId, result.AcceptText, result.PreviewText, result.NextText, result.UndoDescription, result.Scope); + + adjustmentKinds.SortAndRemoveDuplicates(); + return (result, adjustmentKinds.ToImmutableAndClear()); } } diff --git a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 0cf2a98e170..6429654a57c 100644 --- a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -312,7 +312,14 @@ public ValueTask GetUpdatesAsync(CancellationToken canc [Obsolete] public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) - => throw new NotImplementedException(); + { + // StreamJsonRpc may use this overload when the method is invoked with empty parameters. Call the new implementation instead. + + if (!runningProjects.IsEmpty) + throw new NotImplementedException(); + + return GetUpdatesAsync(ImmutableArray.Empty, cancellationToken); + } public async ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs index a58f500f26d..80b8038ef7e 100644 --- a/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs +++ b/src/roslyn/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs @@ -38,7 +38,14 @@ public ValueTask GetUpdatesAsync(CancellationToken canc [Obsolete] public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) - => throw new NotImplementedException(); + { + // StreamJsonRpc may use this overload when the method is invoked with empty parameters. Call the new implementation instead. + + if (!runningProjects.IsEmpty) + throw new NotImplementedException(); + + return GetUpdatesAsync(ImmutableArray.Empty, cancellationToken); + } public ValueTask GetUpdatesAsync(ImmutableArray runningProjects, CancellationToken cancellationToken) => service.GetUpdatesAsync(runningProjects, cancellationToken); diff --git a/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs b/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs index 8ad860c76ae..9a4df990d5d 100644 --- a/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/Editor/EditorLayerExtensionManager.cs @@ -51,8 +51,8 @@ protected override void HandleNonCancellationException(object provider, Exceptio if (provider is CodeFixProvider or CodeRefactoringProvider - or CodeRefactorings.FixAllProvider - or CodeFixes.FixAllProvider + or RefactorAllProvider + or FixAllProvider or CompletionProvider) { if (!IsIgnored(provider)) diff --git a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs index 86b847e0f24..411f1f3843b 100644 --- a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs +++ b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptFindUsagesContext.cs @@ -124,7 +124,7 @@ internal sealed class VSTypeScriptSourceReferenceItem( VSTypeScriptDocumentSpan sourceSpan, VSTypeScriptSymbolUsageInfo symbolUsageInfo) { - internal readonly SourceReferenceItem UnderlyingObject = new SourceReferenceItem( + internal readonly SourceReferenceItem UnderlyingObject = new( definition.UnderlyingObject, sourceSpan.ToDocumentSpan(), classifiedSpans: null, symbolUsageInfo.UnderlyingObject); public VSTypeScriptDocumentSpan GetSourceSpan() diff --git a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs index 6d7a280f8f1..9d7ce76763c 100644 --- a/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs +++ b/src/roslyn/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDebugLocationInfoWrapper.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; internal readonly struct VSTypeScriptDebugLocationInfoWrapper(string name, int lineOffset) { - internal readonly DebugLocationInfo UnderlyingObject = new DebugLocationInfo(name, lineOffset); + internal readonly DebugLocationInfo UnderlyingObject = new(name, lineOffset); public readonly string Name => UnderlyingObject.Name; public readonly int LineOffset => UnderlyingObject.LineOffset; diff --git a/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs b/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs index 817006e99a8..e963568c775 100644 --- a/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs +++ b/src/roslyn/src/EditorFeatures/Core/Host/IPreviewPaneService.cs @@ -10,5 +10,5 @@ namespace Microsoft.CodeAnalysis.Editor.Host; internal interface IPreviewPaneService : IWorkspaceService { - object GetPreviewPane(DiagnosticData diagnostic, IReadOnlyList previewContent); + object GetPreviewPane(DiagnosticData? diagnostic, IReadOnlyList previewContent); } diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs index 45e07d043b6..9a7eac5caa9 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/AbstractInlineRenameUndoManager.cs @@ -28,8 +28,8 @@ protected sealed class ActiveSpanState protected readonly InlineRenameService InlineRenameService; protected readonly Dictionary UndoManagers = []; - protected readonly Stack UndoStack = new Stack(); - protected readonly Stack RedoStack = new Stack(); + protected readonly Stack UndoStack = new(); + protected readonly Stack RedoStack = new(); protected ActiveSpanState initialState; protected ActiveSpanState currentState; protected bool updatePending = false; diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs index ad9beefdab3..892c6e7ec1e 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameConflictTag.cs @@ -11,7 +11,7 @@ internal sealed class RenameConflictTag : TextMarkerTag // Only used for theming, does not need localized internal const string TagId = "RoslynRenameConflictTag"; - public static readonly RenameConflictTag Instance = new RenameConflictTag(); + public static readonly RenameConflictTag Instance = new(); private RenameConflictTag() : base(TagId) diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs index f5c9069b1fc..43a4a17fad0 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFieldBackgroundAndBorderTag.cs @@ -11,7 +11,7 @@ internal sealed class RenameFieldBackgroundAndBorderTag : TextMarkerTag // Only used for theming, does not need localized internal const string TagId = "RoslynRenameFieldBackgroundAndBorderTag"; - public static readonly RenameFieldBackgroundAndBorderTag Instance = new RenameFieldBackgroundAndBorderTag(); + public static readonly RenameFieldBackgroundAndBorderTag Instance = new(); private RenameFieldBackgroundAndBorderTag() : base(TagId) diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs index aaca154fea4..80f29d7b20c 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/HighlightTags/RenameFixupTag.cs @@ -11,7 +11,7 @@ internal sealed class RenameFixupTag : TextMarkerTag // Only used for theming, does not need localized internal const string TagId = "RoslynRenameFixupTag"; - public static readonly RenameFixupTag Instance = new RenameFixupTag(); + public static readonly RenameFixupTag Instance = new(); private RenameFixupTag() : base(TagId) diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs index cea4219fd87..115dcac76a9 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs @@ -163,8 +163,8 @@ private void RaiseSpansChanged() internal IEnumerable GetRenameTrackingSpans() => _referenceSpanToLinkedRenameSpanMap.Values.Where(r => r.Type != RenameSpanKind.None).Concat(_conflictResolutionRenameTrackingSpans); - internal IEnumerable GetEditableSpansForSnapshot(ITextSnapshot snapshot) - => _referenceSpanToLinkedRenameSpanMap.Values.Where(r => r.Type != RenameSpanKind.None).Select(r => r.TrackingSpan.GetSpan(snapshot)); + internal ImmutableArray GetEditableSpansForSnapshot(ITextSnapshot snapshot) + => _referenceSpanToLinkedRenameSpanMap.Values.SelectAsArray(r => r.Type != RenameSpanKind.None, r => r.TrackingSpan.GetSpan(snapshot)); internal void SetReferenceSpans(IEnumerable spans) { @@ -281,7 +281,7 @@ internal void ApplyReplacementText(bool updateSelection = true) _session.UndoManager.ApplyCurrentState( _subjectBuffer, s_propagateSpansEditTag, - _referenceSpanToLinkedRenameSpanMap.Values.Where(r => r.Type != RenameSpanKind.None).Select(r => r.TrackingSpan)); + _referenceSpanToLinkedRenameSpanMap.Values.SelectAsArray(r => r.Type != RenameSpanKind.None, r => r.TrackingSpan)); if (updateSelection && _activeSpan.HasValue && this.ActiveTextView != null) { diff --git a/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs b/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs index 527c1415118..05c37286b62 100644 --- a/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs +++ b/src/roslyn/src/EditorFeatures/Core/InlineRename/UI/InlineRenameAdornmentManager.cs @@ -32,8 +32,7 @@ internal sealed class InlineRenameAdornmentManager : IDisposable private readonly IAdornmentLayer _adornmentLayer; - private static readonly ConditionalWeakTable s_createdViewModels = - new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_createdViewModels = new(); public InlineRenameAdornmentManager( InlineRenameService renameService, diff --git a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs index 1ad77dd9e78..f6a4e288eca 100644 --- a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs +++ b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/FilterSet.cs @@ -38,7 +38,7 @@ internal sealed class FilterSet(bool supportExpander) // to create a filter list covering a completion session. private static readonly ImmutableArray s_filters; - private BitVector32 _vector = new BitVector32(); + private BitVector32 _vector = new(); private static readonly int s_expanderMask; private readonly bool _supportExpander = supportExpander; diff --git a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs index 1448aeb9c27..e30be495f55 100644 --- a/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/ItemManagerProvider.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncComplet [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class ItemManagerProvider(RecentItemsManager recentItemsManager, EditorOptionsService editorOptionsService) : IAsyncCompletionItemManagerProvider { - private readonly ItemManager _instance = new ItemManager(recentItemsManager, editorOptionsService); + private readonly ItemManager _instance = new(recentItemsManager, editorOptionsService); public IAsyncCompletionItemManager? GetOrCreate(ITextView textView) { diff --git a/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs b/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs index 2a4a36e2ef6..724310e9611 100644 --- a/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/Interactive/InertClassifierProvider.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.Interactive; [TextViewRole(PredefinedInteractiveTextViewRoles.InteractiveTextViewRole)] internal sealed partial class InertClassifierProvider : IClassifierProvider { - private static readonly object s_classificationsKey = new object(); + private static readonly object s_classificationsKey = new(); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs b/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs index f9a8f3a4600..854c840c821 100644 --- a/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs +++ b/src/roslyn/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToPreviewServiceFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; internal sealed class DefaultNavigateToPreviewServiceFactory : IWorkspaceServiceFactory { private readonly Lazy _singleton = - new Lazy(() => new DefaultNavigateToPreviewService()); + new(() => new DefaultNavigateToPreviewService()); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs b/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs index 4e918d3b822..ef1a21e22c4 100644 --- a/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs +++ b/src/roslyn/src/EditorFeatures/Core/NavigateTo/NavigateToItemDisplay.cs @@ -65,12 +65,12 @@ private ReadOnlyCollection CreateDescriptionItems() var items = new List { - new DescriptionItem( + new( new ReadOnlyCollection( new[] { new DescriptionRun("Project:", bold: true) }), new ReadOnlyCollection( new[] { new DescriptionRun(document.Project.Name) })), - new DescriptionItem( + new( new ReadOnlyCollection( new[] { new DescriptionRun("File:", bold: true) }), new ReadOnlyCollection( diff --git a/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs b/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs index a99ad91eb46..1c5a1a0ad9f 100644 --- a/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs +++ b/src/roslyn/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs @@ -195,7 +195,7 @@ void IDisposable.Dispose() _cancellationTokenSource.Cancel(); } - public TestAccessor GetTestAccessor() => new TestAccessor(this); + public TestAccessor GetTestAccessor() => new(this); private void OnEventSourceChanged(object? sender, TaggerEventArgs e) { diff --git a/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs b/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs index 7447f9e4611..79debbd5a4e 100644 --- a/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs +++ b/src/roslyn/src/EditorFeatures/Core/Notification/EditorNotificationServiceFactory.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Notification; [Shared] internal sealed class EditorNotificationServiceFactory : IWorkspaceServiceFactory { - private static readonly object s_gate = new object(); + private static readonly object s_gate = new(); private static EditorDialogService s_singleton; diff --git a/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs b/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs index acfddd3000d..43ce1af2c89 100644 --- a/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs +++ b/src/roslyn/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.UndoPrimitive.cs @@ -21,7 +21,7 @@ internal sealed partial class RenameTrackingTaggerProvider /// private sealed class UndoPrimitive(ITextBuffer textBuffer, int trackingSessionId, bool shouldRestoreStateOnUndo) : ITextUndoPrimitive { - private readonly WeakReference _weakTextBuffer = new WeakReference(textBuffer); + private readonly WeakReference _weakTextBuffer = new(textBuffer); private readonly int _trackingSessionId = trackingSessionId; private readonly bool _shouldRestoreStateOnUndo = shouldRestoreStateOnUndo; diff --git a/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs b/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs index 2eed1c7d183..34782c9caff 100644 --- a/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs +++ b/src/roslyn/src/EditorFeatures/Core/Shared/Extensions/HostWorkspaceServicesExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Host; @@ -11,6 +12,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -83,7 +85,7 @@ private static Dictionary CreateContentTypeMap(SolutionServices l => hostWorkspaceServices.GetLanguageServices(l).GetRequiredService().GetDefaultContentType().TypeName); } - internal static IList SelectMatchingExtensionValues( + internal static ImmutableArray SelectMatchingExtensionValues( this SolutionServices workspaceServices, IEnumerable> items, IContentType contentType) @@ -92,7 +94,9 @@ internal static IList SelectMatchingExtensionValues( if (items == null) return []; - return [.. items.Where(lazy => LanguageMatches(lazy.Metadata.Language, contentType, workspaceServices)).Select(lazy => lazy.Value)]; + return items.SelectAsArray( + lazy => LanguageMatches(lazy.Metadata.Language, contentType, workspaceServices), + lazy => lazy.Value); } private static bool LanguageMatches( diff --git a/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs b/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs index 7beba24fb50..13a63042dc4 100644 --- a/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs +++ b/src/roslyn/src/EditorFeatures/Core/SignatureHelp/Presentation/Signature.cs @@ -217,7 +217,7 @@ private static IList AddOptionalBrackets(bool isOptional, IList { - new TaggedText(TextTags.Punctuation, "[") + new(TextTags.Punctuation, "[") }; result.AddRange(list); result.Add(new TaggedText(TextTags.Punctuation, "]")); diff --git a/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs b/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs index b26c393f1f3..3a633de5891 100644 --- a/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs +++ b/src/roslyn/src/EditorFeatures/Core/SplitComment/SplitCommentOptionsStorage.cs @@ -9,5 +9,5 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.SplitComment; internal sealed class SplitCommentOptionsStorage { public static PerLanguageOption2 Enabled = - new PerLanguageOption2("dotnet_split_comments", defaultValue: true); + new("dotnet_split_comments", defaultValue: true); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs deleted file mode 100644 index ecef4935b1d..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/Copilot/FlavoredSuggestedAction.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class SuggestedActionWithNestedFlavors -{ - /// - /// Suggested action for additional custom hyperlink in the lightbulb preview pane. - /// Each can define custom - /// code actions for adding these custom hyperlinks to the preview, - /// similar to 'Preview Changes' and 'Fix All' hyperlinks that show up for all suggested actions. - /// Note that this suggested action type just wraps the underlying original code action that comes from - /// and gets added to the suggested action set - /// holding all the suggested actions for custom hyperlinks to show in the lightbulb preview pane. - /// - protected sealed class FlavoredSuggestedAction : SuggestedAction - { - private FlavoredSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction originalCodeAction) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, originalCodeAction) - { - } - - public static SuggestedAction Create(SuggestedActionWithNestedFlavors suggestedAction, CodeAction codeAction) - { - return new FlavoredSuggestedAction( - suggestedAction.ThreadingContext, - suggestedAction.SourceProvider, - suggestedAction.Workspace, - suggestedAction.OriginalSolution, - suggestedAction.SubjectBuffer, - suggestedAction.Provider, - codeAction); - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs index c1c6a26dbef..0572cf67963 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/FixAll/FixMultipleOccurrencesService.cs @@ -9,15 +9,15 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// -/// Service to compute and apply code fixes. +/// Service to compute and apply code fixes. /// [ExportWorkspaceService(typeof(IFixMultipleOccurrencesService), ServiceLayer.Host), Shared] [method: ImportingConstructor] @@ -68,7 +68,8 @@ private static async Task GetFixedSolutionAsync( IProgress progress, CancellationToken cancellationToken) { - var fixMultipleCodeAction = new FixMultipleCodeAction(fixAllState, title, waitDialogMessage); + var fixMultipleCodeAction = new RefactorOrFixAllCodeAction( + fixAllState, showPreviewChangesDialog: false, title, waitDialogMessage); Solution newSolution = null; var extensionManager = workspace.Services.GetService(); diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs index 4154ce6be95..ef2d8ba5049 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesCodeAction.cs @@ -11,60 +11,51 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal partial class SuggestedActionWithNestedFlavors +internal sealed partial class EditorSuggestedActionWithNestedFlavors { - private partial class PreviewChangesSuggestedAction + private sealed class PreviewChangesCodeAction( + CodeAction originalCodeAction, + Func> getPreviewResultAsync) : CodeAction { - private sealed class PreviewChangesCodeAction : CodeAction - { - private readonly Workspace _workspace; - private readonly CodeAction _originalCodeAction; - private readonly Func> _getPreviewResultAsync; + private readonly CodeAction _originalCodeAction = originalCodeAction; + private readonly Func> _getPreviewResultAsync = getPreviewResultAsync; + + public override string Title => EditorFeaturesResources.Preview_changes2; - public PreviewChangesCodeAction(Workspace workspace, CodeAction originalCodeAction, Func> getPreviewResultAsync) + private protected override async Task> GetOperationsCoreAsync( + Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + var previewDialogService = originalSolution.Services.GetService(); + if (previewDialogService == null) { - _workspace = workspace; - _originalCodeAction = originalCodeAction; - _getPreviewResultAsync = getPreviewResultAsync; + return []; } - public override string Title => EditorFeaturesResources.Preview_changes2; - - private protected override async Task> GetOperationsCoreAsync( - Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) + var previewResult = await _getPreviewResultAsync(cancellationToken).ConfigureAwait(true); + if (previewResult?.ChangeSummary is not { } changeSummary) { - cancellationToken.ThrowIfCancellationRequested(); - var previewDialogService = _workspace.Services.GetService(); - if (previewDialogService == null) - { - return []; - } - - var previewResult = await _getPreviewResultAsync(cancellationToken).ConfigureAwait(true); - if (previewResult?.ChangeSummary is not { } changeSummary) - { - return []; - } - - var changedSolution = previewDialogService.PreviewChanges( - EditorFeaturesResources.Preview_Changes, - "vs.codefix.previewchanges", - _originalCodeAction.Title, - EditorFeaturesResources.Changes, - CodeAnalysis.Glyph.OpenFolder, - changeSummary.NewSolution, - changeSummary.OldSolution, - showCheckBoxes: false); - - if (changedSolution == null) - { - // User pressed the cancel button. - return []; - } + return []; + } - cancellationToken.ThrowIfCancellationRequested(); - return await _originalCodeAction.GetOperationsAsync(originalSolution, progressTracker, cancellationToken).ConfigureAwait(false); + var changedSolution = previewDialogService.PreviewChanges( + EditorFeaturesResources.Preview_Changes, + "vs.codefix.previewchanges", + _originalCodeAction.Title, + EditorFeaturesResources.Changes, + CodeAnalysis.Glyph.OpenFolder, + changeSummary.NewSolution, + changeSummary.OldSolution, + showCheckBoxes: false); + + if (changedSolution == null) + { + // User pressed the cancel button. + return []; } + + cancellationToken.ThrowIfCancellationRequested(); + return await _originalCodeAction.GetOperationsAsync(originalSolution, progressTracker, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs deleted file mode 100644 index 779754b55e8..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/PreviewChanges/PreviewChangesSuggestedAction.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class SuggestedActionWithNestedFlavors -{ - /// - /// Suggested action for showing the preview-changes dialog. Note: this is only used - /// as a 'flavor' inside CodeFixSuggestionAction and CodeRefactoringSuggestedAction. - /// - private sealed partial class PreviewChangesSuggestedAction : SuggestedAction - { - private PreviewChangesSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - PreviewChangesCodeAction codeAction) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction) - { - } - - public static SuggestedAction Create(SuggestedActionWithNestedFlavors suggestedAction) - { - return new PreviewChangesSuggestedAction( - suggestedAction.ThreadingContext, - suggestedAction.SourceProvider, - suggestedAction.Workspace, - suggestedAction.OriginalSolution, - suggestedAction.SubjectBuffer, - suggestedAction.Provider, - new PreviewChangesCodeAction( - suggestedAction.Workspace, suggestedAction.CodeAction, suggestedAction.GetPreviewResultAsync)); - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs index 79048bfa206..e53eef6742d 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotCodeAction.cs @@ -11,100 +11,91 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Copilot; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal partial class SuggestedActionWithNestedFlavors +internal partial class EditorSuggestedActionWithNestedFlavors { - private partial class RefineUsingCopilotSuggestedAction + /// + /// Code action that triggers Copilot refinement session to add further + /// code changes on top of the changes from the wrapped . + /// + private sealed class RefineUsingCopilotCodeAction( + Solution originalSolution, + CodeAction originalCodeAction, + Diagnostic? primaryDiagnostic, + ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeAction { - /// - /// Code action that triggers Copilot refinement session to add further - /// code changes on top of the changes from the wrapped . - /// - private sealed class RefineUsingCopilotCodeAction( - Solution originalSolution, - CodeAction originalCodeAction, - DiagnosticData? primaryDiagnostic, - ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeAction - { - public override string Title => EditorFeaturesResources.Refine_using_Copilot; + public override string Title => EditorFeaturesResources.Refine_using_Copilot; - protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) - { - // Make sure we don't trigger the refinement session for preview operation - return Task.FromResult(SpecializedCollections.EmptyEnumerable()); - } - - protected override async Task> ComputeOperationsAsync(IProgress progress, CancellationToken cancellationToken) - { - // This method is called when the user has clicked on the 'Refine using Copilot' - // hyperlink in the lightbulb preview. - // We want to bring up Copilot refinement session on top of the code changes - // from the underlying code action. Additionally, if the underlying code action - // came from a prior Copilot code fix suggestion, we also want to pass in the Copilot - // diagnostic to the refinement session. This diagnostic would be mapped to the prior - // Copilot session id to ensure that the Copilot refinement session has the historical - // context on the Copilot conversation that produce the underlying diagnostic/code action. - // - // We have a bunch of checks upfront before we bring up the Copilot refinement: - // - Applying the underlying code action produces a non-null newSolution. - // - The underlying code action produces change(s) to exactly one source document. - // - // TODO: Currently, we start a task to spawn a new Copilot refinement session - // at the end of this method, without waiting for the refinement session to complete. - // Consider if there could be better UX/platform support for such flavored actions - // where clicking on the hyperlink needs to bring up another unrelated UI. + protected override Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + { + // Make sure we don't trigger the refinement session for preview operation + return Task.FromResult(SpecializedCollections.EmptyEnumerable()); + } - var newSolution = await originalCodeAction.GetChangedSolutionInternalAsync(originalSolution, progress, cancellationToken).ConfigureAwait(false); - if (newSolution == null) - return []; + protected override async Task> ComputeOperationsAsync(IProgress progress, CancellationToken cancellationToken) + { + // This method is called when the user has clicked on the 'Refine using Copilot' + // hyperlink in the lightbulb preview. + // We want to bring up Copilot refinement session on top of the code changes + // from the underlying code action. Additionally, if the underlying code action + // came from a prior Copilot code fix suggestion, we also want to pass in the Copilot + // diagnostic to the refinement session. This diagnostic would be mapped to the prior + // Copilot session id to ensure that the Copilot refinement session has the historical + // context on the Copilot conversation that produce the underlying diagnostic/code action. + // + // We have a bunch of checks upfront before we bring up the Copilot refinement: + // - Applying the underlying code action produces a non-null newSolution. + // - The underlying code action produces change(s) to exactly one source document. + // + // TODO: Currently, we start a task to spawn a new Copilot refinement session + // at the end of this method, without waiting for the refinement session to complete. + // Consider if there could be better UX/platform support for such flavored actions + // where clicking on the hyperlink needs to bring up another unrelated UI. - var changes = newSolution.GetChanges(originalSolution); - var changeSummary = new SolutionChangeSummary(originalSolution, newSolution, changes); - if (changeSummary.TotalFilesAffected != 1 - || changeSummary.TotalProjectsAffected != 1 - || changeSummary.NewSolution.GetChangedDocuments(changeSummary.OldSolution).FirstOrDefault() is not { } changedDocumentId) - { - return []; - } + var newSolution = await originalCodeAction.GetChangedSolutionInternalAsync(originalSolution, progress, cancellationToken).ConfigureAwait(false); + if (newSolution == null) + return []; - var oldDocument = changeSummary.OldSolution.GetRequiredDocument(changedDocumentId); - var newDocument = changeSummary.NewSolution.GetRequiredDocument(changedDocumentId); + var changes = newSolution.GetChanges(originalSolution); + var changeSummary = new SolutionChangeSummary(originalSolution, newSolution, changes); + if (changeSummary.TotalFilesAffected != 1 + || changeSummary.TotalProjectsAffected != 1 + || changeSummary.NewSolution.GetChangedDocuments(changeSummary.OldSolution).FirstOrDefault() is not { } changedDocumentId) + { + return []; + } - var convertedPrimaryDiagnostic = primaryDiagnostic != null - ? await primaryDiagnostic.ToDiagnosticAsync(oldDocument.Project, cancellationToken).ConfigureAwait(false) - : null; + var oldDocument = changeSummary.OldSolution.GetRequiredDocument(changedDocumentId); + var newDocument = changeSummary.NewSolution.GetRequiredDocument(changedDocumentId); - cancellationToken.ThrowIfCancellationRequested(); - return [new OpenRefinementSessionOperation(oldDocument, newDocument, convertedPrimaryDiagnostic, copilotCodeAnalysisService)]; - } + cancellationToken.ThrowIfCancellationRequested(); + return [new OpenRefinementSessionOperation(oldDocument, newDocument, primaryDiagnostic, copilotCodeAnalysisService)]; + } - /// - /// A code action operation for trigger Copilot Chat inline refinement session. - /// - private sealed class OpenRefinementSessionOperation( - Document oldDocument, - Document newDocument, - Diagnostic? convertedPrimaryDiagnostic, - ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeActionOperation + /// + /// A code action operation for trigger Copilot Chat inline refinement session. + /// + private sealed class OpenRefinementSessionOperation( + Document oldDocument, + Document newDocument, + Diagnostic? convertedPrimaryDiagnostic, + ICopilotCodeAnalysisService copilotCodeAnalysisService) : CodeActionOperation + { + internal override async Task TryApplyAsync(Workspace workspace, Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) { - internal override async Task TryApplyAsync(Workspace workspace, Solution originalSolution, IProgress progressTracker, CancellationToken cancellationToken) - { - // Trigger the Copilot refinement session in background, passing in the old and new document for - // the base code changes on top of which we want to perform further refinement. - // Note that we do not pass in our cancellation token to the StartRefinementSessionAsync - // call as bringing up the refinement session is a quick operation and the refinement session - // has it's own cancellation token source to allow users to dismiss the session. - // Additionally, we do not want cancellation triggered on the token passed into - // GetChangedSolutionAsync to suddenly dismiss the refinement session UI without user explicitly - // dismissing the session. - await copilotCodeAnalysisService.StartRefinementSessionAsync(oldDocument, newDocument, convertedPrimaryDiagnostic, CancellationToken.None).ConfigureAwait(false); - return true; - } + // Trigger the Copilot refinement session in background, passing in the old and new document for + // the base code changes on top of which we want to perform further refinement. + // Note that we do not pass in our cancellation token to the StartRefinementSessionAsync + // call as bringing up the refinement session is a quick operation and the refinement session + // has it's own cancellation token source to allow users to dismiss the session. + // Additionally, we do not want cancellation triggered on the token passed into + // GetChangedSolutionAsync to suddenly dismiss the refinement session UI without user explicitly + // dismissing the session. + await copilotCodeAnalysisService.StartRefinementSessionAsync(oldDocument, newDocument, convertedPrimaryDiagnostic, CancellationToken.None).ConfigureAwait(false); + return true; } } } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs deleted file mode 100644 index 6c11c57dfeb..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/RefineUsingCopilot/RefineUsingCopilotSuggestedAction.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Copilot; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class SuggestedActionWithNestedFlavors -{ - /// - /// Suggested action for showing the 'Refine with Copilot' hyperlink in lightbulb preview pane. - /// Note: This hyperlink is shown for **all** suggested actions lightbulb preview, i.e. - /// regular code fixes and refactorings, in addition to Copilot suggested code fixes. - /// It wraps the core that invokes into - /// Copilot service to start a Copilot refinement session on top of the document changes - /// from the original code action corresponding to the lightbulb preview. - /// - private sealed partial class RefineUsingCopilotSuggestedAction : SuggestedAction - { - private RefineUsingCopilotSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - RefineUsingCopilotCodeAction codeAction) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction) - { - } - - public static async Task TryCreateAsync(SuggestedActionWithNestedFlavors suggestedAction, CancellationToken cancellationToken) - { - // NOTE: Refine with Copilot functionality is only available for code actions - // that change a source document, such that the option guarding this Refine feature - // is enabled and the Copilot service is available within this VS session. - - if (suggestedAction.OriginalDocument is not Document originalDocument) - return null; - - if (originalDocument.GetLanguageService() is not { } optionsService || - await optionsService.IsRefineOptionEnabledAsync().ConfigureAwait(false) is false) - { - return null; - } - - if (originalDocument.GetLanguageService() is not { } copilotService || - await copilotService.IsAvailableAsync(cancellationToken).ConfigureAwait(false) is false) - { - return null; - } - - return new RefineUsingCopilotSuggestedAction( - suggestedAction.ThreadingContext, - suggestedAction.SourceProvider, - suggestedAction.Workspace, - suggestedAction.OriginalSolution, - suggestedAction.SubjectBuffer, - suggestedAction.Provider, - new RefineUsingCopilotCodeAction( - suggestedAction.OriginalSolution, suggestedAction.CodeAction, suggestedAction.GetDiagnostic(), copilotService)); - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs deleted file mode 100644 index ab37a7d9b60..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionPriorityProvider.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; -using static Microsoft.CodeAnalysis.Editor.Implementation.Suggestions.SuggestedActionPriorityProvider; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Information about de-prioritized analyzers that were moved down from 'Normal' to 'Low' priority bucket. Note that this data is -/// owned by the and shared across priority buckets. -/// -internal sealed class SuggestedActionPriorityProvider( - CodeActionRequestPriority priority, - LowPriorityAnalyzersAndDiagnosticIds lowPriorityAnalyzersAndDiagnosticIds) - : ICodeActionRequestPriorityProvider -{ - public CodeActionRequestPriority? Priority { get; } = priority; - - public struct LowPriorityAnalyzersAndDiagnosticIds() - { - public ConcurrentSet Analyzers { get; } = []; - public ConcurrentSet SupportedDiagnosticIds { get; } = []; - } - - public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - { - lowPriorityAnalyzersAndDiagnosticIds.Analyzers.Add(analyzer); - - foreach (var supportedDiagnostic in analyzer.SupportedDiagnostics) - lowPriorityAnalyzersAndDiagnosticIds.SupportedDiagnosticIds.Add(supportedDiagnostic.Id); - } - - public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - => lowPriorityAnalyzersAndDiagnosticIds.Analyzers.Contains(analyzer); - - public bool HasDeprioritizedAnalyzerSupportingDiagnosticId(ImmutableArray diagnosticIds) - { - foreach (var diagnosticId in diagnosticIds) - { - if (lowPriorityAnalyzersAndDiagnosticIds.SupportedDiagnosticIds.Contains(diagnosticId)) - return true; - } - - return false; - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.cs deleted file mode 100644 index 812bd157a21..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeFixSuggestedAction.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. - -#nullable disable - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Represents light bulb menu item for code fixes. -/// -internal sealed class CodeFixSuggestedAction : SuggestedActionWithNestedFlavors, ICodeFixSuggestedAction, ITelemetryDiagnosticID -{ - public CodeFix CodeFix { get; } - - public CodeFixSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - TextDocument originalDocument, - ITextBuffer subjectBuffer, - CodeFix fix, - object provider, - CodeAction action, - SuggestedActionSet fixAllFlavors) - : base(threadingContext, - sourceProvider, - workspace, - originalDocument, - subjectBuffer, - provider, - action, - fixAllFlavors) - { - CodeFix = fix; - } - - public string GetDiagnosticID() - => CodeFix.PrimaryDiagnostic.GetTelemetryDiagnosticID(); - - protected override DiagnosticData GetDiagnostic() - => CodeFix.GetPrimaryDiagnosticData(); -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs deleted file mode 100644 index 2fab217d08a..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/CodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Represents light bulb menu item for code refactorings. -/// -internal sealed class CodeRefactoringSuggestedAction : SuggestedActionWithNestedFlavors, ICodeRefactoringSuggestedAction -{ - public CodeRefactoringProvider CodeRefactoringProvider { get; } - - public CodeRefactoringSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - TextDocument originalDocument, - ITextBuffer subjectBuffer, - CodeRefactoringProvider provider, - CodeAction codeAction, - SuggestedActionSet fixAllFlavors) - : base(threadingContext, sourceProvider, workspace, originalDocument, subjectBuffer, provider, codeAction, fixAllFlavors) - { - CodeRefactoringProvider = provider; - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.CaretPositionRestorer.cs similarity index 98% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.CaretPositionRestorer.cs index 233fd8af6c0..be3abc31785 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.CaretPositionRestorer.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; -internal partial class SuggestedAction +internal partial class EditorSuggestedAction { internal sealed class CaretPositionRestorer : IDisposable { diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.cs similarity index 82% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.cs index 6c8186609cd..96d9164950b 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedAction.cs @@ -9,6 +9,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using EnvDTE; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; @@ -33,41 +34,25 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// /// Base class for all Roslyn light bulb menu items. /// -internal abstract partial class SuggestedAction : ISuggestedAction3, IEquatable +internal abstract partial class EditorSuggestedAction( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction) : ISuggestedAction3, IEquatable { - protected readonly IThreadingContext ThreadingContext; - protected readonly SuggestedActionsSourceProvider SourceProvider; + protected readonly IThreadingContext ThreadingContext = threadingContext; + protected readonly SuggestedActionsSourceProvider SourceProvider = sourceProvider; - protected readonly Workspace Workspace; - protected readonly Solution OriginalSolution; - protected readonly ITextBuffer SubjectBuffer; + protected readonly Solution OriginalSolution = originalSolution; + protected readonly ITextBuffer SubjectBuffer = subjectBuffer; - protected readonly object Provider; - internal readonly CodeAction CodeAction; + protected readonly object Provider = provider; + internal CodeAction CodeAction { get; } = codeAction; private ICodeActionEditHandlerService EditHandler => SourceProvider.EditHandler; - internal SuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction) - { - Contract.ThrowIfNull(provider); - Contract.ThrowIfNull(codeAction); - - ThreadingContext = threadingContext; - SourceProvider = sourceProvider; - Workspace = workspace; - OriginalSolution = originalSolution; - SubjectBuffer = subjectBuffer; - Provider = provider; - CodeAction = codeAction; - } - public virtual bool TryGetTelemetryId(out Guid telemetryId) { telemetryId = CodeAction.GetTelemetryId(); @@ -118,7 +103,7 @@ private async Task InvokeAsync() { using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Application_Summary, $"Total"); - using var token = SourceProvider.OperationListener.BeginAsyncOperation($"{nameof(SuggestedAction)}.{nameof(Invoke)}"); + using var token = SourceProvider.OperationListener.BeginAsyncOperation($"{nameof(EditorSuggestedAction)}.{nameof(Invoke)}"); using var context = SourceProvider.UIThreadOperationExecutor.BeginExecute( EditorFeaturesResources.Execute_Suggested_Action, CodeAction.Title, allowCancellation: true, showProgress: true); using var scope = context.AddScope(allowCancellation: true, CodeAction.Message); @@ -139,7 +124,7 @@ protected virtual async Task InnerInvokeAsync(IProgress pr using (new CaretPositionRestorer(SubjectBuffer, EditHandler.AssociatedViewService)) { // ConfigureAwait(true) so that CaretPositionRestorer.Dispose runs on the UI thread. - await Workspace.Services.GetService().PerformActionAsync( + await this.OriginalSolution.Services.GetService().PerformActionAsync( Provider, () => InvokeWorkerAsync(progressTracker, cancellationToken)).ConfigureAwait(true); } } @@ -176,8 +161,8 @@ private async Task InvokeWorkerAsync(IProgress progressTra var document = this.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); await EditHandler.ApplyAsync( - Workspace, - OriginalSolution, + this.OriginalSolution.Workspace, + this.OriginalSolution, document, [.. operations], CodeAction.Title, @@ -190,11 +175,11 @@ await EditHandler.ApplyAsync( private void CreateLogProperties(Dictionary map) { // set various correlation info - if (CodeAction is AbstractFixAllCodeFixCodeAction fixSome) + if (CodeAction is RefactorOrFixAllCodeAction fixSome) { // fix all correlation info - map[FixAllLogger.CorrelationId] = fixSome.FixAllState.CorrelationId; - map[FixAllLogger.FixAllScope] = fixSome.FixAllState.Scope.ToString(); + map[FixAllLogger.CorrelationId] = fixSome.RefactorOrFixAllState.CorrelationId; + map[FixAllLogger.FixAllScope] = fixSome.RefactorOrFixAllState.Scope.ToString(); } if (TryGetTelemetryId(out var telemetryId)) @@ -203,10 +188,10 @@ private void CreateLogProperties(Dictionary map) map["TelemetryId"] = telemetryId.ToString(); } - if (this is ITelemetryDiagnosticID diagnosticId) + if (this is ITelemetryDiagnosticID telemetry && telemetry.GetDiagnosticID() is string diagnosticId) { // save what it is actually fixing - map["DiagnosticId"] = diagnosticId.GetDiagnosticID(); + map["DiagnosticId"] = diagnosticId; } } @@ -216,7 +201,7 @@ public string DisplayText { // Underscores will become an accelerator in the VS smart tag. So we double all // underscores so they actually get represented as an underscore in the UI. - var extensionManager = Workspace.Services.GetService(); + var extensionManager = this.OriginalSolution.Services.GetService(); var text = extensionManager.PerformFunction(Provider, () => CodeAction.Title, defaultValue: string.Empty); return text.Replace("_", "__"); } @@ -229,7 +214,8 @@ protected async Task GetPreviewResultAsync(CancellationTo var operations = await GetPreviewOperationsAsync(cancellationToken).ConfigureAwait(true); await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - return await EditHandler.GetPreviewsAsync(Workspace, operations, cancellationToken).ConfigureAwait(true); + return await EditHandler.GetPreviewsAsync( + this.OriginalSolution.Workspace, operations, cancellationToken).ConfigureAwait(true); } public virtual bool HasPreview => false; @@ -285,12 +271,12 @@ ImageMoniker ISuggestedAction.IconMoniker #region IEquatable public bool Equals(ISuggestedAction other) - => Equals(other as SuggestedAction); + => Equals(other as EditorSuggestedAction); public override bool Equals(object obj) - => Equals(obj as SuggestedAction); + => Equals(obj as EditorSuggestedAction); - internal bool Equals(SuggestedAction otherSuggestedAction) + internal bool Equals(EditorSuggestedAction otherSuggestedAction) { if (otherSuggestedAction == null) { @@ -327,4 +313,17 @@ public override int GetHashCode() } #endregion + + public static EditorSuggestedAction CreateTrivialAction( + EditorSuggestedAction action, + CodeAction codeAction) + => new TrivialSuggestedAction(action.ThreadingContext, action.SourceProvider, action.OriginalSolution, action.SubjectBuffer, action.Provider, codeAction); + + private sealed class TrivialSuggestedAction( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction) : EditorSuggestedAction(threadingContext, sourceProvider, originalSolution, subjectBuffer, provider, codeAction); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionForRefactorOrFixAll.cs similarity index 62% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionForRefactorOrFixAll.cs index 1a3371ab12d..0d371bf07a9 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionForRefactorOrFixAll.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; @@ -16,39 +17,32 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// /// Suggested action for fix all occurrences for a code fix or a code refactoring. /// -internal abstract class AbstractFixAllSuggestedAction : SuggestedAction +internal sealed class EditorSuggestedActionForRefactorOrFixAll( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + IRefactorOrFixAllState fixAllState, + CodeAction originalCodeAction, + string? diagnosticTelemetryId) + : EditorSuggestedAction(threadingContext, + sourceProvider, + originalSolution, + subjectBuffer, + fixAllState.FixAllProvider, + new RefactorOrFixAllCodeAction(fixAllState, showPreviewChangesDialog: true)), + ITelemetryDiagnosticID { - public CodeAction OriginalCodeAction { get; } + public string? GetDiagnosticID() => diagnosticTelemetryId; - public IFixAllState FixAllState { get; } - - protected AbstractFixAllSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - IFixAllState fixAllState, - CodeAction originalCodeAction, - AbstractFixAllCodeAction fixAllCodeAction) - : base(threadingContext, - sourceProvider, - workspace, - originalSolution, - subjectBuffer, - fixAllState.FixAllProvider, - fixAllCodeAction) - { - OriginalCodeAction = originalCodeAction; - FixAllState = fixAllState; - } + internal new RefactorOrFixAllCodeAction CodeAction => (RefactorOrFixAllCodeAction)base.CodeAction; public override bool TryGetTelemetryId(out Guid telemetryId) { // We get the telemetry id for the original code action we are fixing, // not the special 'FixAllCodeAction'. that is the .CodeAction this // SuggestedAction is pointing at. - telemetryId = OriginalCodeAction.GetTelemetryId(FixAllState.Scope); + telemetryId = originalCodeAction.GetTelemetryId(fixAllState.Scope); return true; } @@ -57,7 +51,7 @@ protected override async Task InnerInvokeAsync( { await this.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var fixAllKind = FixAllState.FixAllKind; + var fixAllKind = fixAllState.FixAllKind; var functionId = fixAllKind switch { FixAllKind.CodeFix => FunctionId.CodeFixes_FixAllOccurrencesSession, @@ -65,7 +59,7 @@ protected override async Task InnerInvokeAsync( _ => throw ExceptionUtilities.UnexpectedValue(fixAllKind) }; - using (Logger.LogBlock(functionId, FixAllLogger.CreateCorrelationLogMessage(FixAllState.CorrelationId), cancellationToken)) + using (Logger.LogBlock(functionId, FixAllLogger.CreateCorrelationLogMessage(fixAllState.CorrelationId), cancellationToken)) { await base.InnerInvokeAsync(progress, cancellationToken).ConfigureAwait(false); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedActions.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedActions.cs similarity index 50% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedActions.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedActions.cs index c5c6aee3c8b..feba7ae1499 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedActions.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedActions.cs @@ -20,50 +20,30 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// Lightbulb item that has child items that should be displayed as 'menu items' /// (as opposed to 'flavor items'). /// -internal sealed class SuggestedActionWithNestedActions : SuggestedAction +internal sealed class EditorSuggestedActionWithNestedActions( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + Solution originalSolution, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction, + ImmutableArray nestedActionSets) + : EditorSuggestedAction(threadingContext, sourceProvider, originalSolution, subjectBuffer, provider, codeAction) { - public readonly ImmutableArray NestedActionSets; + public readonly ImmutableArray NestedActionSets = nestedActionSets; - public SuggestedActionWithNestedActions( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction, - ImmutableArray nestedActionSets) - : base(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction) - { - Debug.Assert(!nestedActionSets.IsDefaultOrEmpty); - NestedActionSets = nestedActionSets; - } - - public SuggestedActionWithNestedActions( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction, - SuggestedActionSet nestedActionSet) - : this(threadingContext, sourceProvider, workspace, originalSolution, subjectBuffer, provider, codeAction, [nestedActionSet]) - { - } - - public override bool HasActionSets => true; + public sealed override bool HasActionSets => true; public sealed override Task> GetActionSetsAsync(CancellationToken cancellationToken) => Task.FromResult>(NestedActionSets); - protected override Task InnerInvokeAsync(IProgress progress, CancellationToken cancellationToken) + protected sealed override Task InnerInvokeAsync(IProgress progress, CancellationToken cancellationToken) { // A code action with nested actions is itself never invokable. So just do nothing if this ever gets asked. // Report a message in debug and log a watson exception so that if this is hit we can try to narrow down how // this happened. - Debug.Fail($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(SuggestedActionWithNestedActions)}"); - FatalError.ReportAndCatch(new InvalidOperationException($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(SuggestedActionWithNestedActions)}"), ErrorSeverity.Critical); + Debug.Fail($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(EditorSuggestedActionWithNestedActions)}"); + FatalError.ReportAndCatch(new InvalidOperationException($"{nameof(InnerInvokeAsync)} should not be called on a {nameof(EditorSuggestedActionWithNestedActions)}"), ErrorSeverity.Critical); return Task.CompletedTask; } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedFlavors.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs similarity index 60% rename from src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedFlavors.cs rename to src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs index 9e606d7d0e4..074bbe05c5c 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionWithNestedFlavors.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/EditorSuggestedActionWithNestedFlavors.cs @@ -2,67 +2,61 @@ // 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 System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; /// -/// Base type for all SuggestedActions that have 'flavors'. 'Flavors' are child actions that -/// are presented as simple links, not as menu-items, in the light-bulb. Examples of 'flavors' -/// include 'preview changes' (for refactorings and fixes) and 'fix all in document, project, solution' -/// (for refactorings and fixes). +/// Type for all SuggestedActions that have 'flavors'. 'Flavors' are child actions that are presented as simple links, +/// not as menu-items, in the light-bulb. Examples of 'flavors' include 'preview changes' (for refactorings and fixes) +/// and 'fix all in document, project, solution' (for refactorings and fixes). /// -/// Because all derivations support 'preview changes', we bake that logic into this base type. +/// Supports 'preview changes' for all changes. /// -internal abstract partial class SuggestedActionWithNestedFlavors : SuggestedAction, ISuggestedActionWithFlavors +internal sealed partial class EditorSuggestedActionWithNestedFlavors( + IThreadingContext threadingContext, + SuggestedActionsSourceProvider sourceProvider, + TextDocument originalDocument, + ITextBuffer subjectBuffer, + object provider, + CodeAction codeAction, + SuggestedActionSet? fixAllFlavors, + ImmutableArray diagnostics) + : EditorSuggestedAction(threadingContext, + sourceProvider, + originalDocument.Project.Solution, + subjectBuffer, + provider, + codeAction), ISuggestedActionWithFlavors, ITelemetryDiagnosticID { - private readonly SuggestedActionSet _fixAllFlavors; + private readonly SuggestedActionSet? _fixAllFlavors = fixAllFlavors; + private readonly ImmutableArray _diagnostics = diagnostics; + private ImmutableArray _nestedFlavors; - public TextDocument OriginalDocument { get; } - - public SuggestedActionWithNestedFlavors( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - TextDocument originalDocument, - ITextBuffer subjectBuffer, - object provider, - CodeAction codeAction, - SuggestedActionSet fixAllFlavors) - : base(threadingContext, - sourceProvider, - workspace, - originalDocument.Project.Solution, - subjectBuffer, - provider, - codeAction) - { - _fixAllFlavors = fixAllFlavors; - OriginalDocument = originalDocument; - } + public TextDocument OriginalDocument { get; } = originalDocument; /// /// HasActionSets is always true because we always know we provide 'preview changes'. /// public sealed override bool HasActionSets => true; - public sealed override async Task> GetActionSetsAsync(CancellationToken cancellationToken) + public sealed override async Task?> GetActionSetsAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -71,7 +65,7 @@ public sealed override async Task> GetActionSets if (_nestedFlavors.IsDefault) { - var extensionManager = this.Workspace.Services.GetService(); + var extensionManager = this.OriginalSolution.Services.GetRequiredService(); // Note: We must ensure that CreateAllFlavorsAsync does not perform any expensive // long running operations as it will be invoked when a lightbulb preview is brought @@ -89,17 +83,12 @@ public sealed override async Task> GetActionSets private async Task> CreateAllFlavorsAsync(CancellationToken cancellationToken) { - var builder = ArrayBuilder.GetInstance(); + using var _ = ArrayBuilder.GetInstance(out var builder); - var primarySuggestedActionSet = await GetPrimarySuggestedActionSetAsync(cancellationToken).ConfigureAwait(false); - builder.Add(primarySuggestedActionSet); + builder.Add(await GetPrimarySuggestedActionSetAsync(cancellationToken).ConfigureAwait(false)); + builder.AddIfNotNull(_fixAllFlavors); - if (_fixAllFlavors != null) - { - builder.Add(_fixAllFlavors); - } - - return builder.ToImmutableAndFree(); + return builder.ToImmutableAndClear(); } private async Task GetPrimarySuggestedActionSetAsync(CancellationToken cancellationToken) @@ -114,20 +103,41 @@ private async Task GetPrimarySuggestedActionSetAsync(Cancell // Note that flavored suggested actions for Fix All operations are added in a separate // suggested action set by our caller, we don't add them here. - using var _ = ArrayBuilder.GetInstance(out var suggestedActions); - var previewChangesAction = PreviewChangesSuggestedAction.Create(this); + using var _ = ArrayBuilder.GetInstance(out var suggestedActions); + var previewChangesAction = CreateTrivialAction( + this, new PreviewChangesCodeAction(this.CodeAction, this.GetPreviewResultAsync)); suggestedActions.Add(previewChangesAction); - var refineUsingCopilotAction = await RefineUsingCopilotSuggestedAction.TryCreateAsync(this, cancellationToken).ConfigureAwait(false); + var refineUsingCopilotAction = await TryCreateRefineSuggestedActionAsync().ConfigureAwait(false); if (refineUsingCopilotAction != null) suggestedActions.Add(refineUsingCopilotAction); foreach (var action in this.CodeAction.AdditionalPreviewFlavors) - { - suggestedActions.Add(FlavoredSuggestedAction.Create(this, action)); - } + suggestedActions.Add(CreateTrivialAction(this, action)); return new SuggestedActionSet(categoryName: null, actions: suggestedActions.ToImmutable()); + + async Task TryCreateRefineSuggestedActionAsync() + { + if (this.OriginalDocument is not Document originalDocument) + return null; + + if (originalDocument.GetLanguageService() is not { } optionsService || + await optionsService.IsRefineOptionEnabledAsync().ConfigureAwait(false) is false) + { + return null; + } + + if (originalDocument.GetLanguageService() is not { } copilotService || + await copilotService.IsAvailableAsync(cancellationToken).ConfigureAwait(false) is false) + { + return null; + } + + return CreateTrivialAction( + this, new RefineUsingCopilotCodeAction( + this.OriginalSolution, this.CodeAction, _diagnostics.FirstOrDefault(), copilotService)); + } } // HasPreview is called synchronously on the UI thread. In order to avoid blocking the UI thread, @@ -139,14 +149,14 @@ private async Task GetPrimarySuggestedActionSetAsync(Cancell // 'null' / empty collection from within GetPreviewAsync(). public override bool HasPreview => true; - public override async Task GetPreviewAsync(CancellationToken cancellationToken) + public override async Task GetPreviewAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Light bulb will always invoke this function on the UI thread. this.ThreadingContext.ThrowIfNotOnUIThread(); - var previewPaneService = Workspace.Services.GetService(); + var previewPaneService = this.OriginalSolution.Services.GetService(); if (previewPaneService == null) { return null; @@ -154,10 +164,10 @@ public override async Task GetPreviewAsync(CancellationToken cancellatio // after this point, this method should only return at GetPreviewPane. otherwise, DifferenceViewer will leak // since there is no one to close the viewer - var preferredDocumentId = Workspace.GetDocumentIdInCurrentContext(SubjectBuffer.AsTextContainer()); - var preferredProjectId = preferredDocumentId?.ProjectId; + var preferredDocumentId = this.OriginalDocument.Id; + var preferredProjectId = preferredDocumentId.ProjectId; - var extensionManager = this.Workspace.Services.GetService(); + var extensionManager = this.OriginalSolution.Services.GetRequiredService(); var previewContents = await extensionManager.PerformFunctionAsync(Provider, async cancellationToken => { // We need to stay on UI thread after GetPreviewResultAsync() so that TakeNextPreviewAsync() @@ -180,8 +190,10 @@ public override async Task GetPreviewAsync(CancellationToken cancellatio // GetPreviewPane() needs to run on the UI thread. this.ThreadingContext.ThrowIfNotOnUIThread(); - return previewPaneService.GetPreviewPane(GetDiagnostic(), previewContents); + var diagnosticData = DiagnosticData.Create(_diagnostics.FirstOrDefault(), this.OriginalDocument.Project); + return previewPaneService.GetPreviewPane(diagnosticData, previewContents!); } - protected virtual DiagnosticData GetDiagnostic() => null; + public string? GetDiagnosticID() + => _diagnostics.FirstOrDefault()?.GetTelemetryDiagnosticID(); } diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.cs deleted file mode 100644 index 45a7656998b..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.FixAllCodeAction.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. - -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -internal partial class FixAllCodeFixSuggestedAction -{ - private sealed partial class FixAllCodeAction : AbstractFixAllCodeFixCodeAction - { - public FixAllCodeAction(IFixAllState fixAllState) - : base(fixAllState, showPreviewChangesDialog: true) - { - } - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs deleted file mode 100644 index 519dcd53adb..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeFixSuggestedAction.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Suggested action for fix all occurrences code fix. Note: this is only used -/// as a 'flavor' inside CodeFixSuggestionAction. -/// -internal sealed partial class FixAllCodeFixSuggestedAction : AbstractFixAllSuggestedAction, ITelemetryDiagnosticID, IFixAllCodeFixSuggestedAction -{ - public Diagnostic Diagnostic { get; } - - public FixAllCodeFixSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - IFixAllState fixAllState, - Diagnostic diagnostic, - CodeAction originalCodeAction) - : base(threadingContext, - sourceProvider, - workspace, - originalSolution, - subjectBuffer, - fixAllState, - originalCodeAction, - new FixAllCodeAction(fixAllState)) - { - Diagnostic = diagnostic; - } - - public string GetDiagnosticID() - => Diagnostic.GetTelemetryDiagnosticID(); -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs deleted file mode 100644 index b8a66badd9a..00000000000 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActions/FixAllCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; -using Microsoft.VisualStudio.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; - -/// -/// Suggested action for fix all occurrences for a code refactoring. Note: this is only used -/// as a 'flavor' inside CodeRefactoringSuggestionAction. -/// -internal sealed class FixAllCodeRefactoringSuggestedAction : AbstractFixAllSuggestedAction, IFixAllCodeRefactoringSuggestedAction -{ - public FixAllCodeRefactoringSuggestedAction( - IThreadingContext threadingContext, - SuggestedActionsSourceProvider sourceProvider, - Workspace workspace, - Solution originalSolution, - ITextBuffer subjectBuffer, - IFixAllState fixAllState, - CodeAction originalCodeAction) - : base(threadingContext, - sourceProvider, - workspace, - originalSolution, - subjectBuffer, - fixAllState, - originalCodeAction, - new FixAllCodeRefactoringCodeAction(fixAllState)) - { - } -} diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs index b65af94657a..59e14821b31 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource.cs @@ -199,33 +199,30 @@ private void OnTextViewClosed(object sender, EventArgs e) if (state is null) return null; - var lowPriorityAnalyzerData = new SuggestedActionPriorityProvider.LowPriorityAnalyzersAndDiagnosticIds(); - foreach (var order in Orderings) { var priority = TryGetPriority(order); Contract.ThrowIfNull(priority); - var priorityProvider = new SuggestedActionPriorityProvider(priority.Value, lowPriorityAnalyzerData); - var result = await GetFixCategoryAsync(priorityProvider).ConfigureAwait(false); + var result = await GetFixCategoryAsync(priority).ConfigureAwait(false); if (result != null) return result; } return null; - async Task GetFixCategoryAsync(ICodeActionRequestPriorityProvider priorityProvider) + async Task GetFixCategoryAsync(CodeActionRequestPriority? priority) { if (state.Target.Owner._codeFixService != null && state.Target.SubjectBuffer.SupportsCodeFixes()) { var result = await state.Target.Owner._codeFixService.GetMostSevereFixAsync( - document, range.Span.ToTextSpan(), priorityProvider, cancellationToken).ConfigureAwait(false); + document, range.Span.ToTextSpan(), priority, cancellationToken).ConfigureAwait(false); if (result != null) { Logger.Log(FunctionId.SuggestedActions_HasSuggestedActionsAsync); - return result.FirstDiagnostic.Severity switch + return result.Diagnostics.First().Severity switch { DiagnosticSeverity.Hidden or DiagnosticSeverity.Info or DiagnosticSeverity.Warning => PredefinedSuggestedActionCategoryNames.CodeFix, diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs index 19090e94817..42f148b097b 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSourceProvider.cs @@ -42,9 +42,9 @@ internal sealed partial class SuggestedActionsSourceProvider : ISuggestedActions { public static readonly ImmutableArray Orderings = [DefaultOrderings.Highest, DefaultOrderings.Default, DefaultOrderings.Low, DefaultOrderings.Lowest]; - private static readonly Guid s_CSharpSourceGuid = new Guid("b967fea8-e2c3-4984-87d4-71a38f49e16a"); - private static readonly Guid s_visualBasicSourceGuid = new Guid("4de30e93-3e0c-40c2-a4ba-1124da4539f6"); - private static readonly Guid s_xamlSourceGuid = new Guid("a0572245-2eab-4c39-9f61-06a6d8c5ddda"); + private static readonly Guid s_CSharpSourceGuid = new("b967fea8-e2c3-4984-87d4-71a38f49e16a"); + private static readonly Guid s_visualBasicSourceGuid = new("4de30e93-3e0c-40c2-a4ba-1124da4539f6"); + private static readonly Guid s_xamlSourceGuid = new("a0572245-2eab-4c39-9f61-06a6d8c5ddda"); private readonly IThreadingContext _threadingContext; private readonly ICodeRefactoringService _codeRefactoringService; diff --git a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs index 1b60d0cac2d..5a24330764a 100644 --- a/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs +++ b/src/roslyn/src/EditorFeatures/Core/Suggestions/SuggestedActionsSource_Async.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -19,10 +18,11 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; -using Microsoft.CodeAnalysis.UnifiedSuggestions; +using Microsoft.CodeAnalysis.Suggestions; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -100,16 +100,10 @@ await document.Project.Solution.Services.GetRequiredService>.GetInstance(out var pendingActionSets); + using var _ = PooledDictionary>.GetInstance(out var pendingActionSets); try { - // Keep track of the diagnostic analyzers that have been deprioritized across calls to the - // diagnostic engine. We'll run them once we get around to the low-priority bucket. We want to - // keep track of this *across* calls to each priority. So we create this set outside of the loop and - // then pass it continuously from one priority group to the next. - var lowPriorityAnalyzerData = new SuggestedActionPriorityProvider.LowPriorityAnalyzersAndDiagnosticIds(); - using var _2 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total"); // Collectors are in priority order. So just walk them from highest to lowest. @@ -121,8 +115,7 @@ await document.Project.Solution.Services.GetRequiredService ArrayBuilder.GetInstance()); + var builder = pendingActionSets.GetOrAdd(actualSetPriority, _ => ArrayBuilder.GetInstance()); builder.Add(set); } else @@ -192,21 +185,21 @@ await document.Project.Solution.Services.GetRequiredService GetCodeFixesAndRefactoringsAsync( + private async IAsyncEnumerable GetCodeFixesAndRefactoringsAsync( ReferenceCountedDisposable state, ISuggestedActionCategorySet requestedActionCategories, TextDocument document, SnapshotSpan range, TextSpan? selection, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, int currentActionCount, [EnumeratorCancellation] CancellationToken cancellationToken) { var target = state.Target; var owner = target.Owner; var subjectBuffer = target.SubjectBuffer; - var workspace = document.Project.Solution.Workspace; - var supportsFeatureService = workspace.Services.GetRequiredService(); + var solution = document.Project.Solution; + var supportsFeatureService = solution.Services.GetRequiredService(); var fixesTask = GetCodeFixesAsync(); var refactoringsTask = GetRefactoringsAsync(); @@ -224,9 +217,9 @@ private async IAsyncEnumerable GetCodeFixesAndRefactoringsAs yield break; - async Task> GetCodeFixesAsync() + async Task> GetCodeFixesAsync() { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetCodeFixesAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priority.GetPriorityInt()}.{nameof(GetCodeFixesAsync)}"); if (owner._codeFixService == null || !supportsFeatureService.SupportsCodeFixes(target.SubjectBuffer) || @@ -236,13 +229,13 @@ async Task> GetCodeFixesAsync() } return await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync( - workspace, owner._codeFixService, document, range.Span.ToTextSpan(), - priorityProvider, cancellationToken).ConfigureAwait(false); + owner._codeFixService, document, range.Span.ToTextSpan(), + priority, cancellationToken).ConfigureAwait(false); } - async Task> GetRefactoringsAsync() + async Task> GetRefactoringsAsync() { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetRefactoringsAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.SuggestedAction_Summary, $"Total.Pri{priority.GetPriorityInt()}.{nameof(GetRefactoringsAsync)}"); if (!selection.HasValue) { @@ -260,7 +253,7 @@ async Task> GetRefactoringsAsync() // 'CodeActionRequestPriority.Lowest' is reserved for suppression/configuration code fixes. // No code refactoring should have this request priority. - if (priorityProvider.Priority == CodeActionRequestPriority.Lowest) + if (priority == CodeActionRequestPriority.Lowest) return []; // If we are computing refactorings outside the 'Refactoring' context, i.e. for example, from the lightbulb under a squiggle or selection, @@ -268,60 +261,66 @@ async Task> GetRefactoringsAsync() var filterOutsideSelection = !requestedActionCategories.Contains(PredefinedSuggestedActionCategoryNames.Refactoring); return await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( - workspace, owner._codeRefactoringService, document, selection.Value, priorityProvider.Priority, + owner._codeRefactoringService, document, selection.Value, priority, filterOutsideSelection, cancellationToken).ConfigureAwait(false); } - [return: NotNullIfNotNull(nameof(unifiedSuggestedActionSet))] - SuggestedActionSet? ConvertToSuggestedActionSet(UnifiedSuggestedActionSet? unifiedSuggestedActionSet, TextDocument originalDocument) + VisualStudio.Language.Intellisense.SuggestedActionSet ConvertToSuggestedActionSet(CodeAnalysis.Suggestions.SuggestedActionSet unifiedSuggestedActionSet, TextDocument originalDocument) { - // May be null in cases involving CodeFixSuggestedActions since FixAllFlavors may be null. - if (unifiedSuggestedActionSet == null) - return null; - - var originalSolution = unifiedSuggestedActionSet.OriginalSolution; - - return new SuggestedActionSet( + return new VisualStudio.Language.Intellisense.SuggestedActionSet( unifiedSuggestedActionSet.CategoryName, unifiedSuggestedActionSet.Actions.SelectAsArray(set => ConvertToSuggestedAction(set)), unifiedSuggestedActionSet.Title, ConvertToSuggestedActionSetPriority(unifiedSuggestedActionSet.Priority), unifiedSuggestedActionSet.ApplicableToSpan?.ToSpan()); - ISuggestedAction ConvertToSuggestedAction(IUnifiedSuggestedAction unifiedSuggestedAction) - => unifiedSuggestedAction switch + ISuggestedAction ConvertToSuggestedAction(SuggestedAction action) + { + if (action.RefactorOrFixAllState != null) + { + return new EditorSuggestedActionForRefactorOrFixAll( + _threadingContext, owner, originalDocument.Project.Solution, subjectBuffer, + action.RefactorOrFixAllState, action.CodeAction, + action.Diagnostics.FirstOrDefault()?.GetTelemetryDiagnosticID()); + } + else if (!action.NestedActionSets.IsEmpty) + { + return new EditorSuggestedActionWithNestedActions( + _threadingContext, owner, document.Project.Solution, subjectBuffer, + action.Provider, action.CodeAction, + action.NestedActionSets.SelectAsArray(s => ConvertToSuggestedActionSet(s, originalDocument))); + } + else { - UnifiedCodeFixSuggestedAction codeFixAction => new CodeFixSuggestedAction( - _threadingContext, owner, codeFixAction.Workspace, originalDocument, subjectBuffer, - codeFixAction.CodeFix, codeFixAction.Provider, codeFixAction.OriginalCodeAction, - ConvertToSuggestedActionSet(codeFixAction.FixAllFlavors, originalDocument)), - UnifiedCodeRefactoringSuggestedAction codeRefactoringAction => new CodeRefactoringSuggestedAction( - _threadingContext, owner, codeRefactoringAction.Workspace, originalDocument, subjectBuffer, - codeRefactoringAction.CodeRefactoringProvider, codeRefactoringAction.OriginalCodeAction, - ConvertToSuggestedActionSet(codeRefactoringAction.FixAllFlavors, originalDocument)), - UnifiedFixAllCodeFixSuggestedAction fixAllAction => new FixAllCodeFixSuggestedAction( - _threadingContext, owner, fixAllAction.Workspace, originalSolution, subjectBuffer, - fixAllAction.FixAllState, fixAllAction.Diagnostic, fixAllAction.OriginalCodeAction), - UnifiedFixAllCodeRefactoringSuggestedAction fixAllCodeRefactoringAction => new FixAllCodeRefactoringSuggestedAction( - _threadingContext, owner, fixAllCodeRefactoringAction.Workspace, originalSolution, subjectBuffer, - fixAllCodeRefactoringAction.FixAllState, fixAllCodeRefactoringAction.OriginalCodeAction), - UnifiedSuggestedActionWithNestedActions nestedAction => new SuggestedActionWithNestedActions( - _threadingContext, owner, nestedAction.Workspace, originalSolution, subjectBuffer, - nestedAction.Provider ?? this, nestedAction.OriginalCodeAction, - nestedAction.NestedActionSets.SelectAsArray(s => ConvertToSuggestedActionSet(s, originalDocument))), - _ => throw ExceptionUtilities.Unreachable() + return new EditorSuggestedActionWithNestedFlavors( + _threadingContext, owner, originalDocument, subjectBuffer, action.Provider, action.CodeAction, + ConvertFlavors(action.Flavors), action.Diagnostics); + } + } + + static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(CodeActionPriority priority) + => priority switch + { + CodeActionPriority.Lowest => SuggestedActionSetPriority.None, + CodeActionPriority.Low => SuggestedActionSetPriority.Low, + CodeActionPriority.Default => SuggestedActionSetPriority.Medium, + CodeActionPriority.High => SuggestedActionSetPriority.High, + _ => throw ExceptionUtilities.Unreachable(), }; - } - static SuggestedActionSetPriority ConvertToSuggestedActionSetPriority(CodeActionPriority priority) - => priority switch + VisualStudio.Language.Intellisense.SuggestedActionSet? ConvertFlavors(SuggestedActionFlavors? flavors) { - CodeActionPriority.Lowest => SuggestedActionSetPriority.None, - CodeActionPriority.Low => SuggestedActionSetPriority.Low, - CodeActionPriority.Default => SuggestedActionSetPriority.Medium, - CodeActionPriority.High => SuggestedActionSetPriority.High, - _ => throw ExceptionUtilities.Unreachable(), - }; + if (flavors is null) + return null; + + return new( + categoryName: null, + actions: flavors.Value.Actions.SelectAsArray(a => ConvertToSuggestedAction(a)), + title: flavors.Value.Title, + priority: SuggestedActionSetPriority.Low, + applicableToSpan: null); + } + } } } } diff --git a/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs b/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs index a5560e5bb39..3647ecc70b0 100644 --- a/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs +++ b/src/roslyn/src/EditorFeatures/Core/Utilities/NativeMethods.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Editor.Wpf.Utilities; internal static class NativeMethods { - public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF); + public static readonly IntPtr HWND_BROADCAST = new(0xFFFF); public const int WM_SYSCOLORCHANGE = 0x0015; [DllImport("user32.dll", CharSet = CharSet.Auto)] diff --git a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs index 6d5705ade3c..5290e20b9b9 100644 --- a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs +++ b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs @@ -160,7 +160,7 @@ private static string CreateDiagnosticsString(ImmutableArray diagnos private static string GetSignatureDescriptionString(Document document, AddedParameterOrExistingIndex[] signature, int? totalParameters) { - var existingParametersKept = signature.Where(p => p.IsExisting).Select(p => p.OldIndex).ToArray(); + var existingParametersKept = signature.SelectAsArray(p => p.IsExisting, p => p.OldIndex); var removeDescription = string.Empty; if (totalParameters.HasValue) { @@ -176,7 +176,7 @@ private static string GetSignatureDescriptionString(Document document, AddedPara removeDescription = removed.Any() ? string.Format(", Removed: {{{0}}}", string.Join(", ", removed)) : string.Empty; } - var newParametersString = string.Join(",", signature.Where(p => !p.IsExisting).Select(p => p.GetAddedParameter(document))); + var newParametersString = string.Join(",", signature.SelectAsArray(p => !p.IsExisting, p => p.GetAddedParameter(document))); var addDescription = !newParametersString.IsEmpty() ? string.Format(", Added {{{0}}}", newParametersString) : string.Empty; return string.Format("Parameters: <{0}>{1}{2}", string.Join(", ", signature.Select(item => item.ToString())), removeDescription, addDescription); diff --git a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs index 142dad1e5a0..d6e10763eea 100644 --- a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs +++ b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs @@ -61,7 +61,7 @@ protected abstract CodeRefactoringProvider CreateCodeRefactoringProvider( return (actions, actionToInvoke); var fixAllCodeAction = await GetFixAllFixAsync(actionToInvoke, - refactoring.Provider, document, span, fixAllScope.Value).ConfigureAwait(false); + refactoring.Provider, document, span, fixAllScope.Value.ToRefactorAllScope()).ConfigureAwait(false); if (fixAllCodeAction == null) return ([], null); @@ -73,15 +73,15 @@ private static async Task GetFixAllFixAsync( CodeRefactoringProvider provider, Document document, TextSpan selectionSpan, - FixAllScope scope) + RefactorAllScope scope) { - var fixAllProvider = provider.GetFixAllProvider(); - if (fixAllProvider == null || !fixAllProvider.GetSupportedFixAllScopes().Contains(scope)) + var refactorAllProvider = provider.GetRefactorAllProvider(); + if (refactorAllProvider == null || !refactorAllProvider.GetSupportedRefactorAllScopes().Contains(scope)) return null; - var fixAllState = new FixAllState(fixAllProvider, document, selectionSpan, provider, scope, originalCodeAction); - var fixAllContext = new FixAllContext(fixAllState, CodeAnalysisProgress.None, CancellationToken.None); - return await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + var refactorAllState = new RefactorAllState(refactorAllProvider, document, selectionSpan, provider, scope, originalCodeAction); + var refactorAllContext = new RefactorAllContext(refactorAllState, CodeAnalysisProgress.None, CancellationToken.None); + return await refactorAllProvider.GetRefactoringAsync(refactorAllContext).ConfigureAwait(false); } protected override Task> GetDiagnosticsWorkerAsync(EditorTestWorkspace workspace, TestParameters parameters) diff --git a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs index aaef5679e0b..88628f213a6 100644 --- a/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/roslyn/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs @@ -157,7 +157,7 @@ protected static Document GetDocumentAndSelectSpan(EditorTestWorkspace workspace document, diagnostic.Location.SourceSpan, [diagnostic], - (a, d) => fixes.Add(new CodeFix(document.Project, a, d)), + (a, d) => fixes.Add(new CodeFix(a, d)), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context); @@ -256,7 +256,7 @@ internal async Task TestSpansAsync( else { var diagnostics = await GetDiagnosticsAsync(workspace, ps); - actualTextSpans = diagnostics.Where(d => d.Id == diagnosticId).Select(d => d.Location.SourceSpan).ToSet(); + actualTextSpans = diagnostics.SelectAsArray(d => d.Id == diagnosticId, d => d.Location.SourceSpan).ToSet(); } Assert.True(expectedTextSpans.SetEquals(actualTextSpans)); diff --git a/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 720a63cd339..b828c08c2ff 100644 --- a/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -58,7 +58,7 @@ public async Task TestGetFirstDiagnosticWithFixAsync() var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); var document = project.Documents.Single(); var unused = await fixService.GetMostSevereFixAsync( - document, TextSpan.FromBounds(0, 0), new DefaultCodeActionRequestPriorityProvider(), CancellationToken.None); + document, TextSpan.FromBounds(0, 0), priority: null, CancellationToken.None); var fixer1 = (MockFixer)fixers.Single().Value; var fixer2 = (MockFixer)reference.Fixers.Single(); @@ -142,14 +142,14 @@ public async Task TestGetFixesAsyncForFixableAndNonFixableAnalyzersAsync() // Verify only analyzerWithFix is executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), + CodeActionRequestPriority.Default, cancellationToken: CancellationToken.None); Assert.True(analyzerWithFix.ReceivedCallback); Assert.False(analyzerWithoutFix.ReceivedCallback); // Verify both analyzerWithFix and analyzerWithoutFix are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Lowest'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Lowest), + CodeActionRequestPriority.Lowest, cancellationToken: CancellationToken.None); Assert.True(analyzerWithFix.ReceivedCallback); Assert.True(analyzerWithoutFix.ReceivedCallback); @@ -178,7 +178,7 @@ public async Task TestGetFixesAsyncForDocumentDiagnosticAnalyzerAsync() // Verify both analyzers are executed when GetFixesAsync is invoked with 'CodeActionRequestPriority.Normal'. _ = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), + CodeActionRequestPriority.Default, cancellationToken: CancellationToken.None); Assert.True(documentDiagnosticAnalyzer.ReceivedCallback); } @@ -208,11 +208,11 @@ public async Task TestGetFixesAsyncForGeneratorDiagnosticAsync() Assert.False(codeFix.Called); var fixCollectionSet = await tuple.codeFixService.GetFixesAsync(document, TextSpan.FromBounds(0, 0), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority.Default), + CodeActionRequestPriority.Default, cancellationToken: CancellationToken.None); Assert.True(codeFix.Called); var fixCollection = Assert.Single(fixCollectionSet); - Assert.Equal(MockFixer.Id, fixCollection.FirstDiagnostic.Id); + Assert.Equal(MockFixer.Id, fixCollection.Diagnostics.First().Id); var fix = Assert.Single(fixCollection.Fixes); Assert.Equal(fixTitle, fix.Action.Title); } @@ -303,7 +303,7 @@ private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync( GetDocumentAndExtensionManager(workspace, out var document, out var extensionManager); var unused = await tuple.codeFixService.GetMostSevereFixAsync( - document, TextSpan.FromBounds(0, 0), new DefaultCodeActionRequestPriorityProvider(), CancellationToken.None); + document, TextSpan.FromBounds(0, 0), priority: null, CancellationToken.None); Assert.True(extensionManager.IsDisabled(codefix)); Assert.False(extensionManager.IsIgnored(codefix)); Assert.True(errorReported); @@ -1027,8 +1027,8 @@ void M() // We enable full solution analysis so the 'AnalyzeDocumentAsync' doesn't skip analysis based on whether the document is active/open. workspace.GlobalOptions.SetGlobalOption(SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption, LanguageNames.CSharp, BackgroundAnalysisScope.FullSolution); - var analyzerService = workspace.Services.GetRequiredService(); - var diagnostics = await analyzerService.ForceAnalyzeProjectAsync(sourceDocument.Project, CancellationToken.None); + var analyzerService = (DiagnosticAnalyzerService)workspace.Services.GetRequiredService(); + var diagnostics = await analyzerService.ForceRunCodeAnalysisDiagnosticsAsync(sourceDocument.Project, CancellationToken.None); await VerifyCachedDiagnosticsAsync( sourceDocument, expectedCachedDiagnostic: diagnosticOnFixLineInPriorSnapshot, testSpan, diagnostics); @@ -1060,15 +1060,12 @@ await VerifyCachedDiagnosticsAsync( : root.DescendantNodes().OfType().First().Span; await analyzerService.GetDiagnosticsForIdsAsync( - sourceDocument.Project, [sourceDocument.Id], diagnosticIds: null, shouldIncludeAnalyzer: null, + sourceDocument.Project, [sourceDocument.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); // await diagnosticIncrementalAnalyzer.GetTestAccessor().TextDocumentOpenAsync(sourceDocument); - var lowPriorityAnalyzerData = new SuggestedActionPriorityProvider.LowPriorityAnalyzersAndDiagnosticIds(); - var priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Default, lowPriorityAnalyzerData); - var normalPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, priorityProvider, CancellationToken.None); - priorityProvider = new SuggestedActionPriorityProvider(CodeActionRequestPriority.Low, lowPriorityAnalyzerData); - var lowPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, priorityProvider, CancellationToken.None); + var normalPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, CodeActionRequestPriority.Default, CancellationToken.None); + var lowPriFixes = await tuple.codeFixService.GetFixesAsync(sourceDocument, testSpan, CodeActionRequestPriority.Low, CancellationToken.None); if (expectedNoFixes) { @@ -1077,21 +1074,24 @@ await analyzerService.GetDiagnosticsForIdsAsync( return; } + var deprioritizedAnalyzers = await analyzerService.GetTestAccessor().GetDeprioritizedAnalyzersAsync(sourceDocument.Project); + var deprioritizedIds = await analyzerService.GetTestAccessor().GetDeprioritizedDiagnosticIdsAsync(sourceDocument.Project); + CodeFixCollection expectedFixCollection; if (expectDeprioritization) { Assert.Empty(normalPriFixes); expectedFixCollection = Assert.Single(lowPriFixes); - var lowPriorityAnalyzer = Assert.Single(lowPriorityAnalyzerData.Analyzers); + var lowPriorityAnalyzer = Assert.Single(deprioritizedAnalyzers); Assert.Same(analyzer, lowPriorityAnalyzer); - Assert.Equal(analyzer.SupportedDiagnostics.Select(d => d.Id), lowPriorityAnalyzerData.SupportedDiagnosticIds); + AssertEx.SetEqual(analyzer.SupportedDiagnostics.Select(d => d.Id), deprioritizedIds); } else { expectedFixCollection = Assert.Single(normalPriFixes); Assert.Empty(lowPriFixes); - Assert.Empty(lowPriorityAnalyzerData.Analyzers); - Assert.Empty(lowPriorityAnalyzerData.SupportedDiagnosticIds); + Assert.Empty(deprioritizedAnalyzers); + Assert.Empty(deprioritizedIds); } var fix = expectedFixCollection.Fixes.Single(); diff --git a/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs b/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs index 4057f5db55e..e69784d9634 100644 --- a/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs @@ -265,7 +265,7 @@ public void TestConditionalExpression1() f.IdentifierName("E"), f.IdentifierName("T"), f.IdentifierName("F")), - cs: "(E) ? (T) : (F)", + cs: "E ? T : F", csSimple: "E ? T : F", vb: "If(E, T, F)", vbSimple: "If(E, T, F)"); @@ -412,9 +412,9 @@ public void TestIsExpression() f => f.IsTypeExpression( f.IdentifierName("a"), CreateClass("SomeType")), - cs: "(a) is SomeType", + cs: "a is SomeType", csSimple: "a is SomeType", - vb: "TypeOf (a) Is SomeType", + vb: "TypeOf a Is SomeType", vbSimple: "TypeOf a Is SomeType"); [Fact] @@ -423,7 +423,7 @@ public void TestAsExpression() f => f.TryCastExpression( f.IdentifierName("a"), CreateClass("SomeType")), - cs: "(a) as SomeType", + cs: "a as SomeType", csSimple: "a as SomeType", vb: "TryCast(a, SomeType)", vbSimple: "TryCast(a, SomeType)"); @@ -433,9 +433,9 @@ public void TestNotExpression() => Test( f => f.LogicalNotExpression( f.IdentifierName("a")), - cs: "!(a)", + cs: "!a", csSimple: "!a", - vb: "Not (a)", + vb: "Not a", vbSimple: "Not a"); [Fact] @@ -444,7 +444,7 @@ public void TestCastExpression() f => f.CastExpression( CreateClass("SomeType"), f.IdentifierName("a")), - cs: "(SomeType)(a)", + cs: "(SomeType)a", csSimple: "(SomeType)a", vb: "DirectCast(a, SomeType)", vbSimple: "DirectCast(a, SomeType)"); @@ -454,8 +454,8 @@ public void TestNegateExpression() => Test( f => f.NegateExpression( f.IdentifierName("a")), - cs: "-(a)", + cs: "-a", csSimple: "-a", - vb: "-(a)", + vb: "-a", vbSimple: "-a"); } diff --git a/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionPrecedenceGenerationTests.cs b/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionPrecedenceGenerationTests.cs index 0c8fe8fcd49..51b28262924 100644 --- a/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionPrecedenceGenerationTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/CodeGeneration/ExpressionPrecedenceGenerationTests.cs @@ -190,7 +190,7 @@ public void TestConditionalExpression1() f.IdentifierName("E2")), f.IdentifierName("T"), f.IdentifierName("F")), - cs: "(E1 = (E2)) ? (T) : (F)", + cs: "(E1 = E2) ? T : F", csSimple: "(E1 = E2) ? T : F", vb: null, vbSimple: null); @@ -207,7 +207,7 @@ public void TestConditionalExpression2() f.IdentifierName("E2"), f.IdentifierName("T2"), f.IdentifierName("F2"))), - cs: "((E1) ? (T1) : (F1)) + ((E2) ? (T2) : (F2))", + cs: "(E1 ? T1 : F1) + (E2 ? T2 : F2)", csSimple: "(E1 ? T1 : F1) + (E2 ? T2 : F2)", vb: null, vbSimple: null); @@ -233,9 +233,9 @@ public void TestMemberAccessOffOfIsExpression() f.IdentifierName("a"), CreateClass("SomeType")), f.IdentifierName("M")), - cs: "((a) is SomeType).M", + cs: "(a is SomeType).M", csSimple: "(a is SomeType).M", - vb: "(TypeOf (a) Is SomeType).M", + vb: "(TypeOf a Is SomeType).M", vbSimple: "(TypeOf a Is SomeType).M"); [Fact] @@ -259,7 +259,7 @@ public void TestMemberAccessOffOfAsExpression() f.IdentifierName("a"), CreateClass("SomeType")), f.IdentifierName("M")), - cs: "((a) as SomeType).M", + cs: "(a as SomeType).M", csSimple: "(a as SomeType).M", vb: "(TryCast(a, SomeType)).M", vbSimple: "TryCast(a, SomeType).M"); @@ -284,9 +284,9 @@ public void TestMemberAccessOffOfNotExpression() f.LogicalNotExpression( f.IdentifierName("a")), f.IdentifierName("M")), - cs: "(!(a)).M", + cs: "(!a).M", csSimple: "(!a).M", - vb: "(Not (a)).M", + vb: "(Not a).M", vbSimple: "(Not a).M"); [Fact] @@ -309,7 +309,7 @@ public void TestMemberAccessOffOfCastExpression() CreateClass("SomeType"), f.IdentifierName("a")), f.IdentifierName("M")), - cs: "((SomeType)(a)).M", + cs: "((SomeType)a).M", csSimple: "((SomeType)a).M", vb: "(DirectCast(a, SomeType)).M", vbSimple: "DirectCast(a, SomeType).M"); @@ -322,9 +322,9 @@ public void TestCastOfAddExpression() f.AddExpression( f.IdentifierName("a"), f.IdentifierName("b"))), - cs: "(SomeType)((a) + (b))", + cs: "(SomeType)(a + b)", csSimple: "(SomeType)(a + b)", - vb: "DirectCast((a) + (b), SomeType)", + vb: "DirectCast(a + b, SomeType)", vbSimple: "DirectCast(a + b, SomeType)"); [Fact] @@ -334,9 +334,9 @@ public void TestNegateOfAddExpression() f.AddExpression( f.IdentifierName("a"), f.IdentifierName("b"))), - cs: "-((a) + (b))", + cs: "-(a + b)", csSimple: "-(a + b)", - vb: "-((a) + (b))", + vb: "-(a + b)", vbSimple: "-(a + b)"); [Fact] @@ -346,9 +346,9 @@ public void TestMemberAccessOffOfNegate() f.NegateExpression( f.IdentifierName("a")), f.IdentifierName("M")), - cs: "(-(a)).M", + cs: "(-a).M", csSimple: "(-a).M", - vb: "(-(a)).M", + vb: "(-a).M", vbSimple: "(-a).M"); [Fact] diff --git a/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index ba1ce46177c..463403f8296 100644 --- a/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote.Diagnostics; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -40,7 +39,7 @@ public sealed class DiagnosticAnalyzerServiceTests private static readonly TestComposition s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures; private static AdhocWorkspace CreateWorkspace(Type[] additionalParts = null) - => new AdhocWorkspace(s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(additionalParts).GetHostServices()); + => new(s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(additionalParts).GetHostServices()); private static IGlobalOptionService GetGlobalOptions(Workspace workspace) => workspace.Services.SolutionServices.ExportProvider.GetExportedValue(); @@ -66,7 +65,7 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var service = workspace.Services.GetRequiredService(); var diagnostics = await service.GetDiagnosticsForIdsAsync( - workspace.CurrentSolution.Projects.Single(), documentIds: default, diagnosticIds: null, shouldIncludeAnalyzer: null, + workspace.CurrentSolution.Projects.Single(), documentIds: default, diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -184,7 +183,7 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab // open document workspace.OpenDocument(document.Id); - var diagnostics = await service.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(document.Project, CancellationToken.None); foreach (var diagnostic in diagnostics) { @@ -218,7 +217,7 @@ private static async Task TestAnalyzerAsync( var syntax = false; var semantic = false; - var diagnostics = await service.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(document.Project, CancellationToken.None); (syntax, semantic) = resultSetter(syntax, semantic, diagnostics); @@ -258,7 +257,7 @@ public async Task TestHostAnalyzerErrorNotLeaking() var service = workspace.Services.GetRequiredService(); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -341,7 +340,7 @@ private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace { var service = workspace.Services.GetRequiredService(); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); if (expectAnalyzerExecuted) { @@ -390,7 +389,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); var expectedCount = testMultiple ? 4 : 1; @@ -471,7 +470,7 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS throw ExceptionUtilities.UnexpectedValue(analysisScope); } - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); var diagnostic = diagnostics.SingleOrDefault(); if (includeAnalyzer) @@ -593,7 +592,7 @@ void M() break; } - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); diagnostics = [.. diagnostics .Where(d => d.Id == IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId) @@ -627,31 +626,19 @@ internal async Task TestOnlyRequiredAnalyzerExecutedDuringDiagnosticComputation( // Verify that requesting analyzer diagnostics for analyzer1 does not lead to invoking analyzer2. var analyzer1 = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzer1Id = analyzer1.GetAnalyzerId(); var analyzer2 = new NamedTypeAnalyzer(); - var analyzerIdsToRequestDiagnostics = ImmutableArray.Create(analyzer1Id); var analyzerReference = new AnalyzerImageReference([analyzer1, analyzer2]); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences([analyzerReference])); var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [], analyzerIdsToRequestDiagnostics, - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzer1.Descriptor.Id], AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); Assert.False(analyzer2.ReceivedSymbolCallback); - Assert.Equal(1, diagnosticsMapResults.Diagnostics.Length); - var (actualAnalyzerId, diagnosticMap) = diagnosticsMapResults.Diagnostics.Single(); - Assert.Equal(analyzer1Id, actualAnalyzerId); - Assert.Equal(1, diagnosticMap.Semantic.Length); - var semanticDiagnostics = diagnosticMap.Semantic.Single().Item2; - var diagnostic = Assert.Single(semanticDiagnostics); + var diagnostic = Assert.Single(diagnosticsMapResults); Assert.Equal(analyzer1.Descriptor.Id, diagnostic.Id); - - Assert.Empty(diagnosticMap.Syntax); - Assert.Empty(diagnosticMap.NonLocal); - Assert.Empty(diagnosticMap.Other); } [Theory, CombinatorialData] @@ -661,7 +648,6 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns using var workspace = TestWorkspace.CreateCSharp("class A { }"); var analyzer = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerId = analyzer.GetAnalyzerId(); var analyzerReference = new AnalyzerImageReference([analyzer]); workspace.TryApplyChanges( @@ -670,16 +656,12 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerId], [analyzerId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzer.Descriptor.Id], AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); // In this case, since the analyzer identity is identical, we ran it once - var analyzerResults = diagnosticsMapResults.Diagnostics.Single(); - Assert.Equal(analyzerId, analyzerResults.Item1); - Assert.Equal(1, analyzerResults.Item2.Semantic.Length); + Assert.Single(diagnosticsMapResults); } [Theory, CombinatorialData] @@ -689,16 +671,16 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz using var workspace = TestWorkspace.CreateCSharp("class A { }"); var analyzerProject = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerProjectId = analyzerProject.GetAnalyzerId(); var analyzerProjectReference = new AnalyzerImageReference([analyzerProject]); var analyzerHost = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerHostId = analyzerHost.GetAnalyzerId(); var analyzerHostReference = new AnalyzerImageReference([analyzerHost]); + SerializerService.TestAccessor.AddAnalyzerImageReferences([analyzerProjectReference, analyzerHostReference]); + // AnalyzerImageReference will create a separate AnalyzerImageReference.Id for each instance created, so these will be different. Assert.NotEqual(analyzerProjectReference.Id, analyzerHostReference.Id); - Assert.Equal(analyzerProjectId, analyzerHostId); + Assert.Equal(analyzerProject.GetAnalyzerId(), analyzerHost.GetAnalyzerId()); workspace.TryApplyChanges( workspace.CurrentSolution.WithAnalyzerReferences([analyzerHostReference]) @@ -706,16 +688,13 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerProjectId], [analyzerHostId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzerProject.Descriptor.Id, analyzerHost.Descriptor.Id], + AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); // In this case, since the analyzer reference identity is identical, we ran it once - var analyzerResults = diagnosticsMapResults.Diagnostics.Single(); - Assert.Equal(analyzerHostId, analyzerResults.Item1); - Assert.Equal(1, analyzerResults.Item2.Semantic.Length); + Assert.Single(diagnosticsMapResults); } [Theory, CombinatorialData] @@ -725,15 +704,13 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz using var workspace = TestWorkspace.CreateCSharp("class A { }"); var analyzerProject = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerProjectId = analyzerProject.GetAnalyzerId(); var analyzerProjectReference = CreateAnalyzerReferenceWithSameId(analyzerProject); var analyzerHost = new NamedTypeAnalyzerWithConfigurableEnabledByDefault(isEnabledByDefault: true, DiagnosticSeverity.Warning, throwOnAllNamedTypes: false); - var analyzerHostId = analyzerHost.GetAnalyzerId(); var analyzerHostReference = CreateAnalyzerReferenceWithSameId(analyzerHost); Assert.Equal(analyzerProjectReference.Id, analyzerHostReference.Id); - Assert.Equal(analyzerProjectId, analyzerHostId); + Assert.Equal(analyzerProject.GetAnalyzerId(), analyzerHost.GetAnalyzerId()); workspace.TryApplyChanges( workspace.CurrentSolution.WithAnalyzerReferences(AddExtraReferenceIfNeeded(analyzerHostReference, includeExtraHostReference)) @@ -741,16 +718,13 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_DifferentAnalyz var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerProjectId], [analyzerHostId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzerProject.Descriptor.Id, analyzerHost.Descriptor.Id], + AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); - // In this case, the analyzers are ran twice. This appears to be a bug in SkippedHostAnalyzersInfo.Create, because it calls - // HostDiagnosticAnalyzers.CreateProjectDiagnosticAnalyzersPerReference which already filters out references, it doesn't return any - // references to skip. - Assert.Equal(2, diagnosticsMapResults.Diagnostics.Length); + // We should only get one diagnostic as the two analyzers have the same ID and will be deduped. + Assert.Single(diagnosticsMapResults); static AnalyzerReference CreateAnalyzerReferenceWithSameId(DiagnosticAnalyzer analyzer) { @@ -762,11 +736,13 @@ static AnalyzerReference CreateAnalyzerReferenceWithSameId(DiagnosticAnalyzer an return new TestAnalyzerReferenceByLanguage(map); } - static ImmutableArray AddExtraReferenceIfNeeded(AnalyzerReference mainReference, bool addExtraReference) + ImmutableArray AddExtraReferenceIfNeeded(AnalyzerReference mainReference, bool addExtraReference) { if (addExtraReference) { - return [mainReference, new AnalyzerImageReference([new FieldAnalyzer("FA1234", syntaxTreeAction: false)])]; + var imageReference = new AnalyzerImageReference([new FieldAnalyzer("FA1234", syntaxTreeAction: false)]); + SerializerService.TestAccessor.AddAnalyzerImageReference(imageReference); + return [mainReference, imageReference]; } else { @@ -785,8 +761,6 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns var analyzerProjectReference = CreateAnalyzerReferenceWithSameId(analyzer); var analyzerHostReference = CreateAnalyzerReferenceWithSameId(analyzer); - var analyzerId = analyzer.GetAnalyzerId(); - Assert.Equal(analyzerProjectReference.Id, analyzerHostReference.Id); workspace.TryApplyChanges( @@ -795,13 +769,12 @@ internal async Task TestAnalysisWithAnalyzerInBothProjectAndHost_SameAnalyzerIns var project = workspace.CurrentSolution.Projects.Single(); var document = documentAnalysis ? project.Documents.Single() : null; - var diagnosticsMapResults = await DiagnosticComputer.GetDiagnosticsAsync( - document, project, Checksum.Null, span: null, projectAnalyzerIds: [analyzerId], [analyzerId], - AnalysisKind.Semantic, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - cancellationToken: CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + var diagnosticsMapResults = await service.GetDiagnosticsForIdsAsync( + project, documentIds: default, [analyzer.Descriptor.Id], + AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); - Assert.Equal(1, diagnosticsMapResults.Diagnostics.Length); + Assert.Single(diagnosticsMapResults); static AnalyzerReference CreateAnalyzerReferenceWithSameId(DiagnosticAnalyzer analyzer) { @@ -832,9 +805,8 @@ void M() project = project.AddAdditionalDocument("additional.txt", @"This is an additional file!").Project; var analyzer = new FilterSpanTestAnalyzer(kind); - var analyzerId = analyzer.GetAnalyzerId(); - var analyzerIdsToRequestDiagnostics = ImmutableArray.Create(analyzerId); var analyzerReference = new AnalyzerImageReference([analyzer]); + SerializerService.TestAccessor.AddAnalyzerImageReference(analyzerReference); project = project.AddAnalyzerReference(analyzerReference); workspace.TryApplyChanges(project.Solution); @@ -858,14 +830,12 @@ void M() async Task VerifyCallbackSpanAsync(TextSpan? filterSpan) { var analysisKind = kind is FilterSpanTestAnalyzer.AnalysisKind.SyntaxTree or FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile - ? AnalysisKind.Syntax - : AnalysisKind.Semantic; + ? DiagnosticKind.AnalyzerSyntax + : DiagnosticKind.AnalyzerSemantic; var documentToAnalyze = kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile ? additionalDocument : document; - _ = await DiagnosticComputer.GetDiagnosticsAsync( - documentToAnalyze, project, Checksum.Null, filterSpan, analyzerIdsToRequestDiagnostics, hostAnalyzerIds: [], - analysisKind, new DiagnosticAnalyzerInfoCache(), workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, - CancellationToken.None); + var service = project.Solution.Services.GetRequiredService(); + _ = await service.GetDiagnosticsForSpanAsync( + documentToAnalyze, filterSpan, analysisKind, CancellationToken.None); Assert.Equal(filterSpan, analyzer.CallbackFilterSpan); if (kind == FilterSpanTestAnalyzer.AnalysisKind.AdditionalFile) { @@ -909,18 +879,17 @@ void M() var project = workspace.CurrentSolution.Projects.Single(); var document = project.Documents.Single(); - var diagnosticAnalyzerInfoCache = new DiagnosticAnalyzerInfoCache(); var kind = actionKind == AnalyzerRegisterActionKind.SyntaxTree ? AnalysisKind.Syntax : AnalysisKind.Semantic; - var analyzerIds = ImmutableArray.Create(analyzer.GetAnalyzerId()); // First invoke analysis with cancellation token, and verify canceled compilation and no reported diagnostics. Assert.Empty(analyzer.CanceledCompilations); + var service = project.Solution.Services.GetRequiredService(); try { - _ = await DiagnosticComputer.GetDiagnosticsAsync(document, project, Checksum.Null, span: null, - projectAnalyzerIds: [], analyzerIds, kind, diagnosticAnalyzerInfoCache, workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: analyzer.CancellationToken); + _ = await service.GetDiagnosticsForIdsAsync( + project, [document.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, + analyzer.CancellationToken); throw ExceptionUtilities.Unreachable(); } @@ -928,15 +897,10 @@ void M() { } - Assert.Single(analyzer.CanceledCompilations); - // Then invoke analysis without cancellation token, and verify non-cancelled diagnostic. - var diagnosticsMap = await DiagnosticComputer.GetDiagnosticsAsync(document, project, Checksum.Null, span: null, - projectAnalyzerIds: [], analyzerIds, kind, diagnosticAnalyzerInfoCache, workspace.Services, - logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); - var builder = diagnosticsMap.Diagnostics.Single().diagnosticMap; - var diagnostic = kind == AnalysisKind.Syntax ? builder.Syntax.Single().Item2.Single() : builder.Semantic.Single().Item2.Single(); - Assert.Equal(CancellationTestAnalyzer.DiagnosticId, diagnostic.Id); + var diagnosticsMap = await service.GetDiagnosticsForIdsAsync( + project, [document.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); + Assert.Equal(CancellationTestAnalyzer.DiagnosticId, diagnosticsMap.Single().Id); } [Theory, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1909806")] @@ -964,7 +928,7 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, var service = workspace.Services.GetRequiredService(); - var diagnostics = await service.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await service.ForceRunCodeAnalysisDiagnosticsAsync(project, CancellationToken.None); Assert.NotEmpty(diagnostics); } @@ -1003,9 +967,9 @@ private static (bool, bool) CompilerAnalyzerResultSetter(bool syntax, bool seman private sealed class Analyzer : DiagnosticAnalyzer { - internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor s_semanticRule = new DiagnosticDescriptor("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); - internal static readonly DiagnosticDescriptor s_compilationRule = new DiagnosticDescriptor("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_syntaxRule = new("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_semanticRule = new("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_compilationRule = new("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [s_syntaxRule, s_semanticRule, s_compilationRule]; @@ -1019,9 +983,9 @@ public override void Initialize(AnalysisContext context) private sealed class DisabledByDefaultAnalyzer : DiagnosticAnalyzer { - internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); - internal static readonly DiagnosticDescriptor s_semanticRule = new DiagnosticDescriptor("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); - internal static readonly DiagnosticDescriptor s_compilationRule = new DiagnosticDescriptor("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); + internal static readonly DiagnosticDescriptor s_syntaxRule = new("syntax", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); + internal static readonly DiagnosticDescriptor s_semanticRule = new("semantic", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); + internal static readonly DiagnosticDescriptor s_compilationRule = new("compilation", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: false); public override ImmutableArray SupportedDiagnostics => [s_syntaxRule, s_semanticRule, s_compilationRule]; @@ -1044,7 +1008,7 @@ protected PriorityTestDocumentDiagnosticAnalyzer(int priority) private sealed class LeakDocumentAnalyzer : DocumentDiagnosticAnalyzer { - internal static readonly DiagnosticDescriptor s_syntaxRule = new DiagnosticDescriptor("leak", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); + internal static readonly DiagnosticDescriptor s_syntaxRule = new("leak", "test", "test", "test", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [s_syntaxRule]; diff --git a/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs b/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs index 446a08c4405..c53ddb003aa 100644 --- a/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/DocCommentFormatting/DocCommentFormattingTests.cs @@ -15,8 +15,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.DocCommentFormatting; [Trait(Traits.Feature, Traits.Features.DocCommentFormatting)] public sealed class DocCommentFormattingTests { - private readonly CSharpDocumentationCommentFormattingService _csharpService = new CSharpDocumentationCommentFormattingService(); - private readonly VisualBasicDocumentationCommentFormattingService _vbService = new VisualBasicDocumentationCommentFormattingService(); + private readonly CSharpDocumentationCommentFormattingService _csharpService = new(); + private readonly VisualBasicDocumentationCommentFormattingService _vbService = new(); private void TestFormat(string xmlFragment, string expectedCSharp, string expectedVB) { diff --git a/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs b/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs index b711c7f3475..650609b224c 100644 --- a/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/EditAndContinue/EditAndContinueLanguageServiceTests.cs @@ -319,6 +319,12 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution Assert.True(sessionState.IsSessionActive); +#pragma warning disable CS0612 // Type or member is obsolete + // validate that obsolete overload does not throw for empty array: + _ = await localService.GetUpdatesAsync(runningProjects: ImmutableArray.Empty, CancellationToken.None); + Assert.Equal(++observedDiagnosticVersion, diagnosticRefresher.GlobalStateVersion); +#pragma warning restore + if (commitChanges) { // CommitUpdatesAsync diff --git a/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs b/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs index 54c1bece379..cd34b7ac903 100644 --- a/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/MetadataAsSource/DocCommentFormatterTests.cs @@ -17,8 +17,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.MetadataAsSource; [Trait(Traits.Feature, Traits.Features.MetadataAsSource)] public sealed class DocCommentFormatterTests { - private readonly CSharpDocumentationCommentFormattingService _csharpService = new CSharpDocumentationCommentFormattingService(); - private readonly VisualBasicDocumentationCommentFormattingService _vbService = new VisualBasicDocumentationCommentFormattingService(); + private readonly CSharpDocumentationCommentFormattingService _csharpService = new(); + private readonly VisualBasicDocumentationCommentFormattingService _vbService = new(); private void TestFormat(string docCommentXmlFragment, string expected) => TestFormat(docCommentXmlFragment, expected, expected); diff --git a/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs b/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs index 9ac0a85996e..ed06134b557 100644 --- a/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs +++ b/src/roslyn/src/EditorFeatures/Test/RenameTracking/RenameTrackingTestState.cs @@ -48,7 +48,7 @@ internal sealed class RenameTrackingTestState : IDisposable public MockRefactorNotifyService RefactorNotifyService { get; } private readonly RenameTrackingCodeRefactoringProvider _codeRefactoringProvider; - private readonly RenameTrackingCancellationCommandHandler _commandHandler = new RenameTrackingCancellationCommandHandler(); + private readonly RenameTrackingCancellationCommandHandler _commandHandler = new(); public static RenameTrackingTestState Create( string markup, diff --git a/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs b/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs index 222eb6de371..899e09069ad 100644 --- a/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/Utilities/BloomFilterTests.cs @@ -208,15 +208,15 @@ public void TestCacheCorrectness(bool isCaseSensitive, bool reverse) var comparer = isCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; var allHashSets = new List> { - new HashSet(GenerateStrings(1_000), comparer), - new HashSet(GenerateStrings(1_000).Where((s, i) => i % 1 == 0), comparer), - new HashSet(GenerateStrings(1_000).Where((s, i) => i % 1 == 1), comparer), - new HashSet(GenerateStrings(10_000), comparer), - new HashSet(GenerateStrings(10_000).Where((s, i) => i % 1 == 0), comparer), - new HashSet(GenerateStrings(10_000).Where((s, i) => i % 1 == 1), comparer), - new HashSet(GenerateStrings(100_000), comparer), - new HashSet(GenerateStrings(100_000).Where((s, i) => i % 1 == 0), comparer), - new HashSet(GenerateStrings(100_000).Where((s, i) => i % 1 == 1), comparer), + new(GenerateStrings(1_000), comparer), + new(GenerateStrings(1_000).Where((s, i) => i % 1 == 0), comparer), + new(GenerateStrings(1_000).Where((s, i) => i % 1 == 1), comparer), + new(GenerateStrings(10_000), comparer), + new(GenerateStrings(10_000).Where((s, i) => i % 1 == 0), comparer), + new(GenerateStrings(10_000).Where((s, i) => i % 1 == 1), comparer), + new(GenerateStrings(100_000), comparer), + new(GenerateStrings(100_000).Where((s, i) => i % 1 == 0), comparer), + new(GenerateStrings(100_000).Where((s, i) => i % 1 == 1), comparer), }; // Try the patterns where we're searching smaller filters then larger ones. Then the pattern of larger ones then smaller ones. diff --git a/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs b/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs index b4c444072b0..f6621c4c2d5 100644 --- a/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs +++ b/src/roslyn/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; [UseExportProvider] public sealed class SymbolEquivalenceComparerTests { - public static readonly CS.CSharpCompilationOptions CSharpDllOptions = new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + public static readonly CS.CSharpCompilationOptions CSharpDllOptions = new(OutputKind.DynamicallyLinkedLibrary); public static readonly CS.CSharpCompilationOptions CSharpSignedDllOptions = new CS.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary). WithCryptoKeyFile(SigningTestHelpers.KeyPairFile). WithStrongNameProvider(DefaultDesktopStrongNameProvider); diff --git a/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb b/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb index 8e079af3012..39700b106d5 100644 --- a/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb @@ -293,7 +293,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests ' Make sure we don't crash Dim unused = Await codefixService.GetMostSevereFixAsync( - document, Text.TextSpan.FromBounds(0, 0), New DefaultCodeActionRequestPriorityProvider(), CancellationToken.None) + document, Text.TextSpan.FromBounds(0, 0), priority:=Nothing, CancellationToken.None) End Using End Function diff --git a/src/roslyn/src/EditorFeatures/Test2/Copilot/RoslynProposalAdjusterTests.vb b/src/roslyn/src/EditorFeatures/Test2/Copilot/RoslynProposalAdjusterTests.vb index 950931052da..feaf6acba5b 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Copilot/RoslynProposalAdjusterTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Copilot/RoslynProposalAdjusterTests.vb @@ -4,6 +4,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Copilot +Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic @@ -38,12 +39,23 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Copilot delta -= selectionSpan.Length Next - Dim service = workspace.Services.GetRequiredService(Of ICopilotProposalAdjusterService) - Dim adjustedChanges = Await service.TryAdjustProposalAsync( + Dim service = originalDocument.GetRequiredLanguageService(Of ICopilotProposalAdjusterService) + Dim tuple = Await service.TryAdjustProposalAsync( originalDocument, CopilotUtilities.TryNormalizeCopilotTextChanges(changes), CancellationToken.None) + Dim adjustedChanges = tuple.TextChanges + Dim format = tuple.Format Dim originalDocumentText = Await originalDocument.GetTextAsync() - Dim adjustedDocumentText = originalDocumentText.WithChanges(adjustedChanges) + Dim adjustedDocumentTextAndFinalSpans = CopilotUtilities.GetNewTextAndChangedSpans(originalDocumentText, adjustedChanges) + Dim adjustedDocumentText = adjustedDocumentTextAndFinalSpans.newText + Dim finalSpans = adjustedDocumentTextAndFinalSpans.newSpans + + If format Then + Dim adjustedDocument = originalDocument.WithText(adjustedDocumentText) + Dim formattedDocument = Await Formatter.FormatAsync(adjustedDocument, finalSpans) + Dim formattedText = Await formattedDocument.GetTextAsync() + adjustedDocumentText = formattedText + End If AssertEx.Equal(expected, adjustedDocumentText.ToString()) End Using @@ -202,6 +214,73 @@ class C }") End Function + + Public Async Function TestCSharp_MissingBrace1() As Task + Await TestCSharp(" +class C +{ + void M() + [|{ + Console.WriteLine(1);|] +}", " +using System; + +class C +{ + void M() + { + Console.WriteLine(1); + } +}") + End Function + + + Public Async Function TestCSharp_MissingBrace2() As Task + Await TestCSharp(" +class C +{ + void M() + [|{ + Console.WriteLine(1);|] +", " +using System; + +class C +{ + void M() + { + Console.WriteLine(1); + } +} +") + End Function + + + Public Async Function TestCSharp_MissingBrace3() As Task + Await TestCSharp(" +class C +{ + void M() + [|{ + Console.WriteLine(1);|] + + public void N() { } +} +", " +using System; + +class C +{ + void M() + { + Console.WriteLine(1); + } + + public void N() { } +} +") + End Function + #End Region #Region "Visual Basic" diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTests.vb index 1eb3762d50b..c248f7132d6 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTests.vb @@ -18,7 +18,7 @@ Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics <[UseExportProvider]> - Partial Public MustInherit Class AbstractCrossLanguageUserDiagnosticTest + Partial Public MustInherit Class AbstractCrossLanguageUserDiagnosticTests Private ReadOnly _outputHelper As ITestOutputHelper Protected Sub New(Optional outputHelper As ITestOutputHelper = Nothing) @@ -165,12 +165,12 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics For Each diagnostic In diagnostics Dim fixes = New List(Of CodeFix) - Dim context = New CodeFixContext(document, diagnostic, Sub(a, d) fixes.Add(New CodeFix(document.Project, a, d)), CancellationToken.None) + Dim context = New CodeFixContext(document, diagnostic, Sub(a, d) fixes.Add(New CodeFix(a, d)), CancellationToken.None) providerAndFixer.Item2.RegisterCodeFixesAsync(context).Wait() If fixes.Any() Then result.Add(Tuple.Create(diagnostic, New CodeFixCollection( fixer, diagnostic.Location.SourceSpan, fixes.ToImmutableArrayOrEmpty(), - fixAllState:=Nothing, supportedScopes:=Nothing, firstDiagnostic:=Nothing))) + fixAllState:=Nothing, supportedScopes:=Nothing, ImmutableArray.Create(diagnostic)))) End If Next diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageUserDiagnosticTests.vb similarity index 99% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageUserDiagnosticTests.vb index f5483325495..ed46c706b39 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddImport/AddImportCrossLanguageUserDiagnosticTests.vb @@ -14,8 +14,8 @@ Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddImport - Public Class AddImportCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class AddImportCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Dim fixer As CodeFixProvider diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceCrossLanguageUserDiagnosticTests.vb index 2d460882719..94320f90d76 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AddMissingReference/AddMissingReferenceCrossLanguageUserDiagnosticTests.vb @@ -10,8 +10,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.AddMissingReference Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AddMissingReference - Public Class AddMissingReferenceTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class AddMissingReferenceCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Private Shared ReadOnly s_presentationCoreAssembly As Assembly Private Shared ReadOnly s_presentationFrameworkAssembly As Assembly diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileCrossLanguageUserDiagnosticTests.vb similarity index 97% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileCrossLanguageUserDiagnosticTests.vb index 5a48606469e..b52a1133016 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/AdditionalFileCrossLanguageUserDiagnosticTests.vb @@ -11,8 +11,8 @@ Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.UnitTests Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AdditionalFiles - Public Class AdditionalFileDiagnosticsTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class AdditionalFileCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index f0bfa017e8e..59b974aa904 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -266,7 +266,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests For Each project In workspace.CurrentSolution.Projects actualDiagnostics.AddRange(diagnosticProvider.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None).Result) Next diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index f74a88534c6..f84817340d8 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -535,7 +535,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Assert.Empty(diagnostics) diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Dim diagnostic = diagnostics.First() Assert.True(diagnostic.Id = "AD0001") @@ -606,7 +606,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim document = project.Documents.Single() Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(1, diagnostics.Length) Dim diagnostic = diagnostics.First() @@ -805,7 +805,7 @@ class AnonymousFunctions ' Verify compilation diagnostics are reported with correct location info when asked for project diagnostics. Dim projectDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( project, documentIds:=Nothing, - diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, projectDiagnostics.Length) @@ -955,7 +955,7 @@ class AnonymousFunctions ' Verify no duplicate analysis/diagnostics. Dim document = project.Documents.Single() Dim diagnostics = (Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None)). Select(Function(d) d.Id = NamedTypeAnalyzer.DiagDescriptor.Id) @@ -1050,7 +1050,7 @@ class AnonymousFunctions ' Verify project diagnostics contains diagnostics reported on both partial definitions. Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(2, diagnostics.Length) Dim file1HasDiag = False, file2HasDiag = False @@ -2142,7 +2142,7 @@ class MyClass Assert.Equal(analyzer.Descriptor.Id, descriptors.Single().Id) ' Get cached project diagnostics. - Dim diagnostics = Await diagnosticService.ForceAnalyzeProjectAsync( + Dim diagnostics = Await diagnosticService.ForceRunCodeAnalysisDiagnosticsAsync( project, CancellationToken.None) ' in v2, solution crawler never creates non-local hidden diagnostics. @@ -2152,7 +2152,7 @@ class MyClass ' Get diagnostics explicitly Dim hiddenDiagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Equal(1, hiddenDiagnostics.Length) Assert.Equal(analyzer.Descriptor.Id, hiddenDiagnostics.Single().Id) @@ -2239,7 +2239,7 @@ class C Assert.Equal(1, descriptorsMap.Count) Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds:=Nothing, diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + project, documentIds:=Nothing, diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.Empty(diagnostics) End Using diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageUserDiagnosticTests.vb similarity index 97% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageUserDiagnosticTests.vb index 7d8d9f26651..fef8e8728c4 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateEvent/GenerateEventCrossLanguageUserDiagnosticTests.vb @@ -7,8 +7,8 @@ Imports Microsoft.CodeAnalysis.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateEvent - Public Class GenerateEventCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class GenerateEventCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Return (Nothing, New Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider()) diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageUserDiagnosticTests.vb similarity index 94% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageUserDiagnosticTests.vb index 2bc96ee25ad..a5abc57a173 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateConstructorCrossLanguageUserDiagnosticTests.vb @@ -6,8 +6,8 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateConstructor - Partial Public Class GenerateConstructorCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Partial Public Class GenerateConstructorCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageUserDiagnosticTests.vb similarity index 99% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageUserDiagnosticTests.vb index 71cb4dbecc9..232c94a78bf 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateMethodCrossLanguageUserDiagnosticTests.vb @@ -8,8 +8,8 @@ Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateMethod - Partial Public Class GenerateMethodCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Partial Public Class GenerateMethodCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Public Sub New(outputHelper As ITestOutputHelper) MyBase.New(outputHelper) diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageUserDiagnosticTests.vb similarity index 95% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageUserDiagnosticTests.vb index a41d267ee7f..8c40df74dc2 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/GenerateFromUsage/GenerateVariableCrossLanguageUserDiagnosticTests.vb @@ -10,8 +10,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.GenerateVariable Imports Xunit.Abstractions Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateVariable - Partial Public Class GenerateVariableCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Partial Public Class GenerateVariableCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Public Sub New(outputHelper As ITestOutputHelper) MyBase.New(outputHelper) diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageUserDiagnosticTests.vb index a7f32cacf22..662c6c2a94c 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/ImplementInterface/ImplementInterfaceCrossLanguageUserDiagnosticTests.vb @@ -9,8 +9,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.ImplementInterface Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.ImplementInterface - Public Class ImplementInterfaceCrossLanguageTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class ImplementInterfaceCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then diff --git a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyCrossLanguageUserDiagnosticTests.vb similarity index 98% rename from src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyCrossLanguageUserDiagnosticTests.vb index 9b30c564330..a844a063a80 100644 --- a/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/Diagnostics/UseAutoProperty/UseAutoPropertyCrossLanguageUserDiagnosticTests.vb @@ -9,8 +9,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.UseAutoProperty - Public Class UseAutoPropertyTests - Inherits AbstractCrossLanguageUserDiagnosticTest + Public Class UseAutoPropertyCrossLanguageUserDiagnosticTests + Inherits AbstractCrossLanguageUserDiagnosticTests Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) If language = LanguageNames.CSharp Then diff --git a/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb b/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb index c805f549283..b46d7feda06 100644 --- a/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb +++ b/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb @@ -689,6 +689,161 @@ class C { } Await TestAPIAndFeature(input, kind, host) End Function + + + Public Async Function TestImplicitBaseConstructorReference_CSharp1(kind As TestKind, host As TestHost) As Task + Dim input = + + + +internal abstract class Abstract +{ + protected {|Definition:$$Abstract|}() + { + } +} + +internal abstract class Derived : Abstract +{ + protected [|Derived|]() + { + } +} + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestImplicitBaseConstructorReference_CSharp2(kind As TestKind, host As TestHost) As Task + Dim input = + + + +internal abstract class Abstract +{ + protected {|Definition:$$Abstract|}() + { + } +} + +internal abstract class Derived +{ + protected Derived() + { + } +} + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestImplicitBaseConstructorReference_CSharp3(kind As TestKind, host As TestHost) As Task + Dim input = + + + +internal abstract class Abstract +{ + protected {|Definition:$$Abstract|}() + { + } +} + +internal abstract class Derived : Abstract +{ + protected [|Derived|](int i) + { + } + + protected Derived() : this(0) + { + } +} + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestImplicitBaseConstructorReference_CSharp4(kind As TestKind, host As TestHost) As Task + Dim input = + + + +internal abstract class Abstract +{ + protected {|Definition:$$Abstract|}(int i = 0) + { + } +} + +internal abstract class Derived : Abstract +{ + protected [|Derived|](int i) + { + } + + protected Derived() : this(0) + { + } + + protected Derived(params int[] i) : this(0) + { + } +} + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestImplicitBaseConstructorReference_CSharp5(kind As TestKind, host As TestHost) As Task + Dim input = + + + +internal abstract class Abstract +{ + protected {|Definition:$$Abstract|}(params int[] i) + { + } +} + +internal abstract class Derived : Abstract +{ + protected [|Derived|](int i) + { + } + + protected Derived() : this(0) + { + } + + protected Derived(params int[] i) : this(0) + { + } +} + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + #If False Then @@ -1282,6 +1437,80 @@ End Namespace]]> Await TestAPIAndFeature(input, kind, host) End Function + + + Public Async Function TestImplicitBaseConstructorReference_VisualBasic1(kind As TestKind, host As TestHost) As Task + Dim input = + + + +friend mustinherit class Abstract + protected sub {|Definition:$$New|}() + end sub +end class + +friend class Derived + inherits Abstract + + public sub [|New|]() + end sub +end class + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestImplicitBaseConstructorReference_VisualBasic2(kind As TestKind, host As TestHost) As Task + Dim input = + + + +friend mustinherit class Abstract + protected sub {|Definition:$$New|}() + end sub +end class + +friend class Derived + public sub New() + end sub +end class + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + + Public Async Function TestImplicitBaseConstructorReference_VisualBasic3(kind As TestKind, host As TestHost) As Task + Dim input = + + + +friend mustinherit class Abstract + protected sub {|Definition:$$New|}() + end sub +end class + +friend class Derived + inherits Abstract + + public sub [|New|](i as integer) + end sub + + public sub New() + me.New(0) + end sub +end class + + + + Await TestAPIAndFeature(input, kind, host) + End Function + Public Async Function TestImplicitObjectCreation(kind As TestKind, host As TestHost) As Task diff --git a/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb b/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb index 7e399b64ddb..73e755a53fb 100644 --- a/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.vb @@ -112,7 +112,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences d.Name, d.AnnotatedSpans(key).ToList())).ToList() Dim valueUsageInfoField = key.Substring(ValueUsageInfoKey.Length) Dim actual = GetFileNamesAndSpans( - context.References.Where(Function(r) r.SymbolUsageInfo.ValueUsageInfoOpt?.ToString() = valueUsageInfoField).Select(Function(r) r.SourceSpan)) + context.References.SelectAsArray(Function(r) r.SymbolUsageInfo.ValueUsageInfoOpt?.ToString() = valueUsageInfoField, Function(r) r.SourceSpan)) Assert.Equal(expected, actual) Next diff --git a/src/roslyn/src/EditorFeatures/Test2/GoToBase/VisuaBasicGoToBaseTests.vb b/src/roslyn/src/EditorFeatures/Test2/GoToBase/VisualBasicGoToBaseTests.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/GoToBase/VisuaBasicGoToBaseTests.vb rename to src/roslyn/src/EditorFeatures/Test2/GoToBase/VisualBasicGoToBaseTests.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 0e779ab5f5a..3d37aa88608 100644 --- a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -13078,8 +13078,8 @@ public class Class1 End Using End Function - + Public Async Function TestOverrideInstanceAssignmentOperator(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( + + Public Async Function TestRecommendRefInArgumentList1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , + languageVersion:=LanguageVersion.CSharp7, showCompletionInArgumentLists:=showCompletionInArgumentLists) + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ref") + End Using + End Function + + + + Public Async Function TestRecommendRefInArgumentList2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , + languageVersion:=LanguageVersion.CSharp7, showCompletionInArgumentLists:=showCompletionInArgumentLists) + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("x") + End Using + End Function + + + + Public Async Function TestRecommendRefInArgumentList3(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , + languageVersion:=LanguageVersion.CSharp7, showCompletionInArgumentLists:=showCompletionInArgumentLists) + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ref") + End Using + End Function + + + + Public Async Function TestRecommendRefInArgumentList4(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , + languageVersion:=LanguageVersion.CSharp7, showCompletionInArgumentLists:=showCompletionInArgumentLists) + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ref") + End Using + End Function End Class End Namespace diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_Await.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb rename to src/roslyn/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_Await.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivity.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivitiy.vb rename to src/roslyn/src/EditorFeatures/Test2/IntelliSense/CompletionServiceTests_Exclusivity.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb index 9af6ff6690e..7ab56ceb38b 100644 --- a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb +++ b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/SignatureHelpControllerTests.vb @@ -21,6 +21,7 @@ Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Editor.Commanding.Commands Imports Microsoft.VisualStudio.Text.Projection Imports Moq +Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense @@ -151,7 +152,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense If provider Is Nothing Then items = If(items, CreateItems(1)) - provider = New MockSignatureHelpProvider(items) + provider = New MockSignatureHelpProvider(items.ToImmutableArray()) End If Dim presenter = New Mock(Of IIntelliSensePresenter(Of ISignatureHelpPresenterSession, ISignatureHelpSession))(MockBehavior.Strict) With {.DefaultValue = DefaultValue.Mock} @@ -194,16 +195,16 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Return controller End Function - Private Shared Function CreateItems(count As Integer) As IList(Of SignatureHelpItem) - Return Enumerable.Range(0, count).Select(Function(i) New SignatureHelpItem(isVariadic:=False, documentationFactory:=Nothing, prefixParts:=New List(Of TaggedText), separatorParts:={}, suffixParts:={}, parameters:={}, descriptionParts:={})).ToList() + Private Shared Function CreateItems(count As Integer) As ImmutableArray(Of SignatureHelpItem) + Return Enumerable.Range(0, count).SelectAsArray(Function(i) New SignatureHelpItem(isVariadic:=False, documentationFactory:=Nothing, prefixParts:=New List(Of TaggedText), separatorParts:={}, suffixParts:={}, parameters:={}, descriptionParts:={})) End Function Friend Class MockSignatureHelpProvider Implements ISignatureHelpProvider - Private ReadOnly _items As IList(Of SignatureHelpItem) + Private ReadOnly _items As ImmutableArray(Of SignatureHelpItem) - Public Sub New(items As IList(Of SignatureHelpItem)) + Public Sub New(items As ImmutableArray(Of SignatureHelpItem)) Me._items = items End Sub @@ -212,8 +213,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Public Function GetItemsAsync(document As Document, position As Integer, triggerInfo As SignatureHelpTriggerInfo, options As MemberDisplayOptions, cancellationToken As CancellationToken) As Task(Of SignatureHelpItems) Implements ISignatureHelpProvider.GetItemsAsync GetItemsCount += 1 Return Task.FromResult(If(_items.Any(), - New SignatureHelpItems(_items, TextSpan.FromBounds(position, position), selectedItem:=0, semanticParameterIndex:=0, syntacticArgumentCount:=0, argumentName:=Nothing), - Nothing)) + New SignatureHelpItems(_items, TextSpan.FromBounds(position, position), selectedItem:=0, semanticParameterIndex:=0, syntacticArgumentCount:=0, argumentName:=Nothing), + Nothing)) End Function Public ReadOnly Property TriggerCharacters As ImmutableArray(Of Char) Implements ISignatureHelpProvider.TriggerCharacters diff --git a/src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb b/src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_Await.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb rename to src/roslyn/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_Await.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplificationTests.vb b/src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplifierTest.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplificationTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Simplification/ModuleNameSimplifierTest.vb diff --git a/src/roslyn/src/EditorFeatures/Test2/Simplification/BlockSimplificationTests.vb b/src/roslyn/src/EditorFeatures/Test2/Simplification/SimplificationTests.vb similarity index 100% rename from src/roslyn/src/EditorFeatures/Test2/Simplification/BlockSimplificationTests.vb rename to src/roslyn/src/EditorFeatures/Test2/Simplification/SimplificationTests.vb diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs index e325bf82e54..7e3c9c9b1e6 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Async/Checkpoint.cs @@ -10,7 +10,7 @@ namespace Roslyn.Test.Utilities; public class Checkpoint { - private readonly TaskCompletionSource _tcs = new TaskCompletionSource(); + private readonly TaskCompletionSource _tcs = new(); public Task Task => _tcs.Task; diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs b/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs index e8eafdab087..603403f9cf7 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/BlindAggregatorFactory.cs @@ -119,7 +119,7 @@ public unsafe CoTaskMemPtr() // Singleton instance of the VTable allocated in native memory. Since it's static, the // underlying native memory will be freed when finalizers run at shutdown. - private static readonly CoTaskMemPtr s_instance = new CoTaskMemPtr(); + private static readonly CoTaskMemPtr s_instance = new(); public static IntPtr AddressOfVTable { get { return s_instance.VTablePtr; } } } @@ -128,13 +128,13 @@ public unsafe CoTaskMemPtr() private const int E_NOINTERFACE = unchecked((int)0x80004002); // 00000000-0000-0000-C000-000000000046 - private static readonly Guid s_IUnknownInterfaceGuid = new Guid(0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + private static readonly Guid s_IUnknownInterfaceGuid = new(0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); // 00000003-0000-0000-C000-000000000046 - private static readonly Guid s_IMarshalInterfaceGuid = new Guid(0x00000003, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + private static readonly Guid s_IMarshalInterfaceGuid = new(0x00000003, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); // CBD71F2C-6BC5-4932-B851-B93EB3151386 - private static readonly Guid s_IComWrapperGuid = new Guid("CBD71F2C-6BC5-4932-B851-B93EB3151386"); + private static readonly Guid s_IComWrapperGuid = new("CBD71F2C-6BC5-4932-B851-B93EB3151386"); private static unsafe int QueryInterface(BlindAggregator* pThis, [In] ref Guid riid, out IntPtr pvObject) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs index 883c7bcaca4..7e4e043aa85 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Operators.cs @@ -15,7 +15,7 @@ public static class Operators { [DebuggerStepThrough] private static FormattedClassification New(string text) - => new FormattedClassification(text, ClassificationTypeNames.Operator); + => new(text, ClassificationTypeNames.Operator); public static FormattedClassification Ampersand { get; } = New("&"); public static FormattedClassification AmpersandAmpersand { get; } = New("&&"); diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs index 5858d317f80..870b12fb570 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.OverloadedOperators.cs @@ -15,7 +15,7 @@ public static class OverloadedOperators { [DebuggerStepThrough] private static FormattedClassification New(string text) - => new FormattedClassification(text, ClassificationTypeNames.OperatorOverloaded); + => new(text, ClassificationTypeNames.OperatorOverloaded); public static FormattedClassification Ampersand { get; } = New("&"); public static FormattedClassification AmpersandEquals { get; } = New("&="); diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs index 9cb7bb34162..5b2b4dc4691 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.Punctuation.cs @@ -15,7 +15,7 @@ public static class Punctuation { [DebuggerStepThrough] private static FormattedClassification New(string text) - => new FormattedClassification(text, ClassificationTypeNames.Punctuation); + => new(text, ClassificationTypeNames.Punctuation); public static FormattedClassification OpenCurly { get; } = New("{"); public static FormattedClassification CloseCurly { get; } = New("}"); diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs index 2c8fb423ea4..081f0c60953 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Classification; public static partial class FormattedClassifications { private static FormattedClassification New(string text, string typeName) - => new FormattedClassification(text, typeName); + => new(text, typeName); [DebuggerStepThrough] public static FormattedClassification Struct(string text) diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs deleted file mode 100644 index e8bc36f4040..00000000000 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Extensions/IDiagnosticServiceExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; - -internal static class IDiagnosticServiceExtensions -{ - public static Task> ForceAnalyzeProjectAsync( - this IDiagnosticAnalyzerService service, Project project, CancellationToken cancellationToken) - { - return CodeAnalysisDiagnosticAnalyzerServiceHelpers.ForceCodeAnalysisDiagnosticsAsync( - service, project, new(), cancellationToken); - } -} diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs index 89afb185e0e..67ddac8523f 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs @@ -46,25 +46,25 @@ public abstract partial class AbstractNavigateToTests protected INavigateToItemProvider _provider; protected NavigateToTestAggregator _aggregator; - internal static readonly PatternMatch s_emptyExactPatternMatch = new PatternMatch(PatternMatchKind.Exact, true, true, []); - internal static readonly PatternMatch s_emptyPrefixPatternMatch = new PatternMatch(PatternMatchKind.Prefix, true, true, []); - internal static readonly PatternMatch s_emptySubstringPatternMatch = new PatternMatch(PatternMatchKind.Substring, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseExact, true, true, []); - internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch = new PatternMatch(PatternMatchKind.CamelCasePrefix, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousPrefix, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseSubstring, true, true, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousSubstring, true, true, []); - internal static readonly PatternMatch s_emptyFuzzyPatternMatch = new PatternMatch(PatternMatchKind.Fuzzy, true, true, []); - - internal static readonly PatternMatch s_emptyExactPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Exact, true, false, []); - internal static readonly PatternMatch s_emptyPrefixPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Prefix, true, false, []); - internal static readonly PatternMatch s_emptySubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Substring, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseExact, true, false, []); - internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCasePrefix, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousPrefix, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseSubstring, true, false, []); - internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.CamelCaseNonContiguousSubstring, true, false, []); - internal static readonly PatternMatch s_emptyFuzzyPatternMatch_NotCaseSensitive = new PatternMatch(PatternMatchKind.Fuzzy, true, false, []); + internal static readonly PatternMatch s_emptyExactPatternMatch = new(PatternMatchKind.Exact, true, true, []); + internal static readonly PatternMatch s_emptyPrefixPatternMatch = new(PatternMatchKind.Prefix, true, true, []); + internal static readonly PatternMatch s_emptySubstringPatternMatch = new(PatternMatchKind.Substring, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch = new(PatternMatchKind.CamelCaseExact, true, true, []); + internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch = new(PatternMatchKind.CamelCasePrefix, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch = new(PatternMatchKind.CamelCaseNonContiguousPrefix, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch = new(PatternMatchKind.CamelCaseSubstring, true, true, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch = new(PatternMatchKind.CamelCaseNonContiguousSubstring, true, true, []); + internal static readonly PatternMatch s_emptyFuzzyPatternMatch = new(PatternMatchKind.Fuzzy, true, true, []); + + internal static readonly PatternMatch s_emptyExactPatternMatch_NotCaseSensitive = new(PatternMatchKind.Exact, true, false, []); + internal static readonly PatternMatch s_emptyPrefixPatternMatch_NotCaseSensitive = new(PatternMatchKind.Prefix, true, false, []); + internal static readonly PatternMatch s_emptySubstringPatternMatch_NotCaseSensitive = new(PatternMatchKind.Substring, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseExactPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseExact, true, false, []); + internal static readonly PatternMatch s_emptyCamelCasePrefixPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCasePrefix, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseNonContiguousPrefix, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseSubstringPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseSubstring, true, false, []); + internal static readonly PatternMatch s_emptyCamelCaseNonContiguousSubstringPatternMatch_NotCaseSensitive = new(PatternMatchKind.CamelCaseNonContiguousSubstring, true, false, []); + internal static readonly PatternMatch s_emptyFuzzyPatternMatch_NotCaseSensitive = new(PatternMatchKind.Fuzzy, true, false, []); protected abstract EditorTestWorkspace CreateWorkspace(string content, TestComposition composition); protected abstract string Language { get; } diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs index 96956e50069..6463dd5ea39 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs @@ -18,8 +18,7 @@ private sealed class Callback : INavigateToCallback { private readonly List _itemsReceived = []; - private readonly TaskCompletionSource> _taskCompletionSource = - new TaskCompletionSource>(); + private readonly TaskCompletionSource> _taskCompletionSource = new(); public Callback(INavigateToOptions options) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs index 5cc02d08ec9..ba175205e95 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Semantics/SpeculationAnalyzerTestsBase.cs @@ -16,7 +16,7 @@ public abstract class SpeculationAnalyzerTestsBase : TestBase { protected const string CompilationName = "SemanticModelTestCompilation"; - protected readonly Regex UnderTestRegex = new Regex(@"\[\|(?.*?)\|\]"); + protected readonly Regex UnderTestRegex = new(@"\[\|(?.*?)\|\]"); protected readonly MetadataReference[] References = [ diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs b/src/roslyn/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs index 9c3b93cf5bf..c4baf9342ce 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs @@ -181,7 +181,7 @@ private async Task VerifyCurrentParameterNameWorkerAsync(string markup, string e private static void CompareAndAssertCollectionsAndCurrentParameter( IEnumerable expectedTestItems, SignatureHelpItems actualSignatureHelpItems) { - Assert.True(expectedTestItems.Count() == actualSignatureHelpItems.Items.Count, $"Expected {expectedTestItems.Count()} items, but got {actualSignatureHelpItems.Items.Count}"); + Assert.True(expectedTestItems.Count() == actualSignatureHelpItems.Items.Length, $"Expected {expectedTestItems.Count()} items, but got {actualSignatureHelpItems.Items.Length}"); for (var i = 0; i < expectedTestItems.Count(); i++) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs b/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs index 66cbb7f30d6..fef733630f5 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/TextEditorFactoryExtensions.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests; internal static class TextEditorFactoryExtensions { public static DisposableTextView CreateDisposableTextView(this ITextEditorFactoryService textEditorFactory) - => new DisposableTextView(textEditorFactory.CreateTextView()); + => new(textEditorFactory.CreateTextView()); public static DisposableTextView CreateDisposableTextView(this ITextEditorFactoryService textEditorFactory, ITextBuffer buffer, ImmutableArray roles = default) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs b/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs index ba3bd615d3f..eec26df43e4 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs @@ -20,7 +20,7 @@ public abstract class AbstractTextStructureNavigatorTests protected abstract string ContentType { get; } protected abstract EditorTestWorkspace CreateWorkspace(string code); - protected StringBuilder result = new StringBuilder(); + protected StringBuilder result = new(); protected void AssertExtent(string code) { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs index 54743a35181..f985309e37f 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Threading/WpfTestSharedData.cs @@ -13,7 +13,7 @@ namespace Roslyn.Test.Utilities; public sealed class WpfTestSharedData { - internal static readonly WpfTestSharedData Instance = new WpfTestSharedData(); + internal static readonly WpfTestSharedData Instance = new(); /// /// Holds the last 10 test cases executed: more recent test cases will occur later in the @@ -21,7 +21,7 @@ public sealed class WpfTestSharedData /// private readonly List _recentTestCases = []; - public readonly SemaphoreSlim TestSerializationGate = new SemaphoreSlim(1, 1); + public readonly SemaphoreSlim TestSerializationGate = new(1, 1); private WpfTestSharedData() { diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs index 9ab178aa03c..01bc32e5471 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Utilities/TestCommandExecutionContext.cs @@ -11,5 +11,5 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; internal static class TestCommandExecutionContext { public static CommandExecutionContext Create() - => new CommandExecutionContext(new TestUIThreadOperationContext()); + => new(new TestUIThreadOperationContext()); } diff --git a/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs b/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs index 71041e5c854..6958c6eefd1 100644 --- a/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs +++ b/src/roslyn/src/EditorFeatures/TestUtilities/Workspaces/NoCompilationDocumentDiagnosticAnalyzer.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; [DiagnosticAnalyzer(NoCompilationConstants.LanguageName)] internal sealed class NoCompilationDocumentDiagnosticAnalyzer : DocumentDiagnosticAnalyzer { - public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( + public static readonly DiagnosticDescriptor Descriptor = new( "NC0000", "No Compilation Syntax Error", "No Compilation Syntax Error", "Error", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor]; diff --git a/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs b/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs index 4a1a80c825c..0036699df01 100644 --- a/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs +++ b/src/roslyn/src/EditorFeatures/Text/Extensions.TextBufferContainer.cs @@ -19,7 +19,7 @@ public static partial class Extensions internal sealed class TextBufferContainer : SourceTextContainer { private readonly WeakReference _weakEditorBuffer; - private readonly object _gate = new object(); + private readonly object _gate = new(); private readonly ITextBufferCloneService? _textBufferCloneService; private event EventHandler? EtextChanged; diff --git a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs index 7fc5dde2323..04fde7c6de1 100644 --- a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs +++ b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Text.Shared.Extensions; internal static partial class ITextSnapshotExtensions { public static SnapshotPoint GetPoint(this ITextSnapshot snapshot, int position) - => new SnapshotPoint(snapshot, position); + => new(snapshot, position); public static SnapshotPoint? TryGetPoint(this ITextSnapshot snapshot, int lineNumber, int columnIndex) { @@ -76,13 +76,13 @@ public static bool TryGetPosition(this ITextSnapshot snapshot, int lineNumber, i } public static SnapshotSpan GetSpan(this ITextSnapshot snapshot, int start, int length) - => new SnapshotSpan(snapshot, new Span(start, length)); + => new(snapshot, new Span(start, length)); public static SnapshotSpan GetSpanFromBounds(this ITextSnapshot snapshot, int start, int end) - => new SnapshotSpan(snapshot, Span.FromBounds(start, end)); + => new(snapshot, Span.FromBounds(start, end)); public static SnapshotSpan GetSpan(this ITextSnapshot snapshot, Span span) - => new SnapshotSpan(snapshot, span); + => new(snapshot, span); public static TagSpan GetTagSpan(this ITextSnapshot snapshot, Span span, TTag tag) where TTag : ITag => new(new SnapshotSpan(snapshot, span), tag); diff --git a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs index 073ad13544c..a4bab16fcd9 100644 --- a/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs +++ b/src/roslyn/src/EditorFeatures/Text/Shared/Extensions/TextSpanExtensions.cs @@ -13,13 +13,13 @@ internal static class TextSpanExtensions /// Convert a instance to a . /// public static Span ToSpan(this TextSpan textSpan) - => new Span(textSpan.Start, textSpan.Length); + => new(textSpan.Start, textSpan.Length); /// /// Add an offset to a . /// public static TextSpan MoveTo(this TextSpan textSpan, int offset) - => new TextSpan(textSpan.Start + offset, textSpan.Length); + => new(textSpan.Start + offset, textSpan.Length); /// /// Convert a to a on the given instance diff --git a/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb b/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb index 07bc81522e0..905cc16345e 100644 --- a/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb +++ b/src/roslyn/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb @@ -96,7 +96,7 @@ End Class Private Shared Sub AccessSupportedDiagnostics(analyzer As DiagnosticAnalyzer) Dim diagnosticService = New HostDiagnosticAnalyzers({New AnalyzerImageReference(ImmutableArray.Create(analyzer))}) - diagnosticService.GetDiagnosticDescriptorsPerReference(New DiagnosticAnalyzerInfoCache()) + diagnosticService.GetDiagnosticDescriptorsPerReference(New DiagnosticAnalyzerInfoCache(), project:=Nothing) End Sub diff --git a/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb b/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb index d67ba6133a0..f35ef1e95b0 100644 --- a/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb +++ b/src/roslyn/src/EditorFeatures/VisualBasicTest/Formatting/CodeCleanUpTests.vb @@ -658,7 +658,6 @@ End Class End Function Private Class ModifySolutionFixAll : Inherits FixAllProvider - Public Overrides Function GetSupportedFixAllScopes() As IEnumerable(Of FixAllScope) Return {FixAllScope.Project, FixAllScope.Solution, FixAllScope.Custom} End Function diff --git a/src/roslyn/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs b/src/roslyn/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs index e4d0c46eb2d..9a00d4e7a07 100644 --- a/src/roslyn/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs +++ b/src/roslyn/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs @@ -324,12 +324,6 @@ public sealed override bool IsRefLikeType get { return false; } } - internal sealed override string ExtensionGroupingName - => throw ExceptionUtilities.Unreachable(); - - internal sealed override string ExtensionMarkerName - => throw ExceptionUtilities.Unreachable(); - public sealed override bool IsReadOnly { get { return false; } @@ -363,7 +357,11 @@ internal override bool IsInterface internal override bool HasPossibleWellKnownCloneMethod() => false; internal override bool IsInterpolatedStringHandlerType => false; - internal sealed override ParameterSymbol ExtensionParameter => null; +#nullable enable + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override string? ExtensionGroupingName => null; + internal sealed override string? ExtensionMarkerName => null; +#nullable disable [Conditional("DEBUG")] internal static void VerifyTypeParameters(Symbol container, ImmutableArray typeParameters) diff --git a/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs b/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs index ef3f133a2e2..a77dde5d260 100644 --- a/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; @@ -21,6 +20,11 @@ internal static class AddImportDiagnosticIds /// public const string CS0103 = nameof(CS0103); + /// + /// 'X' does not contain a definition for 'Y' + /// + public const string CS0117 = nameof(CS0117); + /// /// The type or namespace name 'X' does not exist in the namespace 'Y' (are you missing an assembly reference?) /// @@ -152,27 +156,36 @@ internal static class AddImportDiagnosticIds /// public const string CS8415 = nameof(CS8415); - public static ImmutableArray FixableTypeIds = - [CS0103, CS0234, CS0246, CS0305, CS0308, CS0122, CS0307, CS0616, CS1580, CS1581, CS8129, IDEDiagnosticIds.UnboundIdentifierId]; - - public static ImmutableArray FixableDiagnosticIds = - [.. FixableTypeIds, - CS1061, - CS1935, - CS1501, - CS1503, - CS1574, - CS1584, - CS1929, - CS1955, - CS0428, - CS7036, - CS0281, - CS4036, - CS1579, - CS8414, - CS8411, - CS8415]; + public static ImmutableArray FixableDiagnosticIds = [ + CS0103, + CS0117, + CS0234, + CS0246, + CS0305, + CS0308, + CS0122, + CS0307, + CS0616, + CS1580, + CS1581, + CS8129, + CS1061, + CS1935, + CS1501, + CS1503, + CS1574, + CS1584, + CS1929, + CS1955, + CS0428, + CS7036, + CS0281, + CS4036, + CS1579, + CS8414, + CS8411, + CS8415, + IDEDiagnosticIds.UnboundIdentifierId]; } [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddImport), Shared] diff --git a/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs b/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs index 7a7db603929..3227e2a924b 100644 --- a/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs +++ b/src/roslyn/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -22,7 +23,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CSharp.AddImport.AddImportDiagnosticIds; namespace Microsoft.CodeAnalysis.CSharp.AddImport; @@ -41,13 +41,25 @@ protected override bool IsWithinImport(SyntaxNode node) protected override bool CanAddImport(SyntaxNode node, bool allowInHiddenRegions, CancellationToken cancellationToken) => node.CanAddUsingDirectives(allowInHiddenRegions, cancellationToken); - protected override bool CanAddImportForMethod( + protected override bool CanAddImportForMember( string diagnosticId, ISyntaxFacts syntaxFacts, SyntaxNode node, out SimpleNameSyntax nameNode) { nameNode = null; switch (diagnosticId) { + case CS0117: + // We have a name off a type. like int.X + // + // This can only add usings for modern static extension methods. This is only allowed in the `type.Name` case, nothing else. + if (node.Parent is not MemberAccessExpressionSyntax(SyntaxKind.SimpleMemberAccessExpression) simpleMemberAccess || + simpleMemberAccess.Name != node) + { + return false; + } + + break; + case CS7036: case CS0308: case CS0428: @@ -604,42 +616,16 @@ private static CompilationUnitSyntax GetCompilationUnitSyntaxNode( return (CompilationUnitSyntax)contextNode.SyntaxTree.GetRoot(cancellationToken); } - protected override bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken) - { - var leftExpression = syntaxFacts.IsMemberAccessExpression(expression) - ? syntaxFacts.GetExpressionOfMemberAccessExpression(expression) - : syntaxFacts.GetTargetOfMemberBinding(expression); - if (leftExpression == null) - { - if (expression.IsKind(SyntaxKind.CollectionInitializerExpression)) - { - leftExpression = expression.GetAncestor(); - } - else - { - return false; - } - } - - var semanticInfo = semanticModel.GetTypeInfo(leftExpression, cancellationToken); - var leftExpressionType = semanticInfo.Type; - - return IsViableExtensionMethod(method, leftExpressionType); - } - - protected override bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel) + protected override bool IsAddMethodContext( + SyntaxNode node, SemanticModel semanticModel, out SyntaxNode objectCreationExpression) { if (node.Parent.IsKind(SyntaxKind.CollectionInitializerExpression)) { - var objectCreationExpressionSyntax = node.GetAncestor(); - if (objectCreationExpressionSyntax == null) - { - return false; - } - - return true; + objectCreationExpression = node.GetAncestor(); + return objectCreationExpression != null; } + objectCreationExpression = null; return false; } } diff --git a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs index d88a6969b11..839ef19d05b 100644 --- a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.FixAllProvider.cs @@ -11,29 +11,28 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Shared.Extensions; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable; internal sealed partial class EnableNullableCodeRefactoringProvider : CodeRefactoringProvider { - internal sealed override CodeAnalysis.CodeRefactorings.FixAllProvider? GetFixAllProvider() - => FixAllProvider.Instance; + public sealed override CodeAnalysis.CodeRefactorings.RefactorAllProvider? GetRefactorAllProvider() + => RefactorAllProvider.Instance; - private sealed class FixAllProvider : CodeAnalysis.CodeRefactorings.FixAllProvider + private sealed class RefactorAllProvider : CodeAnalysis.CodeRefactorings.RefactorAllProvider { - public static readonly FixAllProvider Instance = new(); + public static readonly RefactorAllProvider Instance = new(); - private FixAllProvider() + private RefactorAllProvider() { } - public override IEnumerable GetSupportedFixAllScopes() - => [FixAllScope.Solution]; + public override IEnumerable GetSupportedRefactorAllScopes() + => [RefactorAllScope.Solution]; - public override Task GetFixAsync(FixAllContext fixAllContext) + public override Task GetRefactoringAsync(RefactorAllContext fixAllContext) { - Debug.Assert(fixAllContext.Scope == FixAllScope.Solution); + Debug.Assert(fixAllContext.Scope == RefactorAllScope.Solution); return Task.FromResult(new FixAllCodeAction(EnableNullableReferenceTypesInSolutionAsync)); async Task EnableNullableReferenceTypesInSolutionAsync( diff --git a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs index 7632a6bbfa8..a3bfcff211d 100644 --- a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs @@ -55,7 +55,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; } - if (!typeStyle.CanConvert()) + if (!typeStyle.CanConvert) { return; } diff --git a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs index 2c7b9b48bb0..ca1c863dbf3 100644 --- a/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs @@ -41,7 +41,7 @@ public UseRecursivePatternsCodeRefactoringProvider() { } - protected override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -579,7 +579,7 @@ when canConvertToSubpattern(name, arg) && !memberAccess.Expression.IsKind(Syntax } } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs b/src/roslyn/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs index 084d7d7065c..5823c30ff8c 100644 --- a/src/roslyn/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs @@ -59,36 +59,26 @@ protected override async Task IsExclusiveAsync(Document document, int posi var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree.IsInNonUserCode(position, cancellationToken)) - { return false; - } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken); token = token.GetPreviousTokenIfTouchingWord(position); if (token.Parent == null) - { return false; - } if (token.Parent.Parent is not ExpressionSyntax expression) - { return false; - } var semanticModel = await document.ReuseExistingSpeculativeModelAsync(expression, cancellationToken).ConfigureAwait(false); var initializedType = semanticModel.GetTypeInfo(expression, cancellationToken).Type; if (initializedType == null) - { return false; - } var enclosingSymbol = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken); // Non-exclusive if initializedType can be initialized as a collection. if (initializedType.CanSupportCollectionInitializer(enclosingSymbol)) - { return false; - } // By default, only our member names will show up. return true; @@ -99,27 +89,21 @@ public override bool IsInsertionTrigger(SourceText text, int characterPosition, public override ImmutableHashSet TriggerCharacters { get; } = CompletionUtilities.CommonTriggerCharacters.Add(' '); - protected override Tuple? GetInitializedType( + protected override (ITypeSymbol type, Location location, bool isObjectInitializer)? GetInitializedType( Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken) { var tree = semanticModel.SyntaxTree; if (tree.IsInNonUserCode(position, cancellationToken)) - { return null; - } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken); token = token.GetPreviousTokenIfTouchingWord(position); if (token.Kind() is not SyntaxKind.CommaToken and not SyntaxKind.OpenBraceToken) - { return null; - } if (token.Parent == null || token.Parent.Parent == null) - { return null; - } // If we got a comma, we can syntactically find out if we're in an ObjectInitializerExpression or WithExpression if (token.Kind() == SyntaxKind.CommaToken && @@ -130,11 +114,9 @@ public override bool IsInsertionTrigger(SourceText text, int characterPosition, var type = GetInitializedType(token, document, semanticModel, cancellationToken); if (type is null) - { return null; - } - return Tuple.Create(type, token.GetLocation()); + return (type, token.GetLocation(), token.Parent.Kind() is not SyntaxKind.WithInitializerExpression); } private static ITypeSymbol? GetInitializedType(SyntaxToken token, Document document, SemanticModel semanticModel, CancellationToken cancellationToken) diff --git a/src/roslyn/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs b/src/roslyn/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs index 6cf761b5058..7cd5324552c 100644 --- a/src/roslyn/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs +++ b/src/roslyn/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; @@ -72,6 +73,8 @@ internal sealed class RefKeywordRecommender() : AbstractSyntacticSingleKeywordRe // SyntaxKind.VolatileKeyword, // fields cannot be byref }; + protected override int PreselectMatchPriority => SymbolMatchPriority.PreferRequiredKeyword; + protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -86,6 +89,50 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context IsAllowsRefStructConstraintContext(context); } + protected override bool ShouldPreselect(CSharpSyntaxContext context, CancellationToken cancellationToken) + { + if (!context.TargetToken.IsConstructorOrMethodParameterArgumentContext()) + return false; + + var parent = context.TargetToken.GetAncestor(n => n is ArgumentListSyntax); + if (parent is not ArgumentListSyntax argumentList) + return false; + + var symbol = context.SemanticModel.GetSymbolInfo(argumentList.GetRequiredParent(), cancellationToken).GetAnySymbol(); + if (symbol is not IMethodSymbol methodSymbol) + return false; + + var parameter = DetermineParameter(); + return parameter?.RefKind is RefKind.Ref or RefKind.RefReadOnlyParameter; + + IParameterSymbol? DetermineParameter() + { + if (context.TargetToken.IsKind(SyntaxKind.ColonToken)) + { + if (context.TargetToken.Parent is NameColonSyntax nameColon) + return methodSymbol.Parameters.FirstOrDefault(p => p.Name == nameColon.Name.Identifier.ValueText); + } + else if (context.TargetToken.Kind() == SyntaxKind.OpenParenToken) + { + return methodSymbol.Parameters.FirstOrDefault(); + } + else if (context.TargetToken.Kind() == SyntaxKind.CommaToken) + { + var index = argumentList.Arguments.GetWithSeparators().IndexOf(context.TargetToken); + if (index >= 0) + { + // Foo(a, Comma index is '1', argument index is '1' + // Foo(a,b, Comma index is '3', argument index is '2' + // Foo(a,b,c, Comma index is '5', argument index is '3' + var argumentIndex = (index + 1) / 2; + return methodSymbol.Parameters.ElementAtOrDefault(argumentIndex); + } + } + + return null; + } + } + private static bool IsAllowsRefStructConstraintContext(CSharpSyntaxContext context) { // cases: diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index a1f8ccb3364..bb3e55ae2a3 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -29,8 +29,8 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace; [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal sealed class ConvertNamespaceCodeRefactoringProvider() : SyntaxEditorBasedCodeRefactoringProvider { - protected override ImmutableArray SupportedFixAllScopes - => [FixAllScope.Project, FixAllScope.Solution]; + protected override ImmutableArray SupportedRefactorAllScopes + => [RefactorAllScope.Project, RefactorAllScope.Solution]; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -84,7 +84,7 @@ private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclarati throw ExceptionUtilities.UnexpectedValue(baseDeclaration.Kind()); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs index 2f87820f961..0fce949f152 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs @@ -57,8 +57,8 @@ public override int GetHashCode() => ExtensionMethodEqualityComparer.Instance.GetHashCode(this); } - internal override FixAllProvider? GetFixAllProvider() - => new ConvertToExtensionFixAllProvider(); + public override RefactorAllProvider? GetRefactorAllProvider() + => new ConvertToExtensionRefactorAllProvider(); private static ExtensionMethodInfo? TryGetExtensionMethodInfo( SemanticModel semanticModel, diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs index bf092c673fa..8ab5ed63b7f 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs @@ -21,16 +21,16 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertToExtension; internal sealed partial class ConvertToExtensionCodeRefactoringProvider { - private sealed class ConvertToExtensionFixAllProvider() - : DocumentBasedFixAllProvider( - [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution, FixAllScope.ContainingType]) + private sealed class ConvertToExtensionRefactorAllProvider() + : DocumentBasedRefactorAllProvider( + [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution, RefactorAllScope.ContainingType]) { - protected override async Task FixAllAsync( - FixAllContext fixAllContext, + protected override async Task RefactorAllAsync( + RefactorAllContext refactorAllContext, Document document, - Optional> fixAllSpans) + Optional> refactorAllSpans) { - var cancellationToken = fixAllContext.CancellationToken; + var cancellationToken = refactorAllContext.CancellationToken; var codeGenerationService = (CSharpCodeGenerationService)document.GetRequiredLanguageService(); @@ -38,7 +38,7 @@ private sealed class ConvertToExtensionFixAllProvider() var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(root, document.Project.Solution.Services); - foreach (var declaration in GetTopLevelClassDeclarations(root, fixAllSpans)) + foreach (var declaration in GetTopLevelClassDeclarations(root, refactorAllSpans)) { // We might hit partial parts that have no extension methods in them. Just skip those. var extensionMethods = GetAllExtensionMethods(semanticModel, declaration, cancellationToken); diff --git a/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs index a4c11272512..c7465e97421 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs @@ -41,7 +41,7 @@ static ConvertStringToRawStringCodeRefactoringProvider() private static readonly ImmutableArray s_convertStringProviders = [ConvertRegularStringToRawStringProvider.Instance, ConvertInterpolatedStringToRawStringProvider.Instance]; - protected override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; protected override CodeActionCleanup Cleanup => CodeActionCleanup.SyntaxOnly; @@ -139,7 +139,7 @@ private static async Task UpdateDocumentAsync( return document.WithSyntaxRoot(root.ReplaceNode(expression, replacement)); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharp/Portable/Copilot/CSharpCopilotProposalAdjusterService.cs b/src/roslyn/src/Features/CSharp/Portable/Copilot/CSharpCopilotProposalAdjusterService.cs new file mode 100644 index 00000000000..bcf757931e4 --- /dev/null +++ b/src/roslyn/src/Features/CSharp/Portable/Copilot/CSharpCopilotProposalAdjusterService.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Copilot; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Copilot; + +[ExportLanguageService(typeof(ICopilotProposalAdjusterService), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpCopilotProposalAdjusterService() : AbstractCopilotProposalAdjusterService +{ + private const string CS1513 = nameof(CS1513); // } expected + + protected override async Task> AddMissingTokensIfAppropriateAsync( + Document originalDocument, ImmutableArray normalizedChanges, CancellationToken cancellationToken) + { + if (normalizedChanges.IsDefaultOrEmpty) + return default; + + var root = await originalDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var originalText = await originalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + + var newText = originalText.WithChanges(normalizedChanges); + var newDocument = originalDocument.WithText(newText); + var newRoot = await newDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + // The amount to adjust things when comparing between the original text and the new text. + var delta = newText.Length - originalText.Length; + + // Check if we introduced a missing close-brace error. + var lastTextChange = normalizedChanges.Last(); + + var lastTextChangePositionInNewText = lastTextChange.Span.End + delta; + if (lastTextChangePositionInNewText < 0) + return default; + + var newDiagnostics = newRoot.GetDiagnostics(); + var closeBraceDiagnostics = newDiagnostics.WhereAsArray( + d => d.Id == CS1513 && d.Location.SourceSpan.Start >= lastTextChangePositionInNewText); + if (closeBraceDiagnostics.IsEmpty) + return default; + + var insertCloseBraceTextChanges = closeBraceDiagnostics.SelectAsArray( + d => new TextChange(new TextSpan(d.Location.SourceSpan.Start - delta, 0), "}")); + + using var _ = ArrayBuilder.GetInstance(normalizedChanges.Length + 1 + insertCloseBraceTextChanges.Length, out var builder); + builder.AddRange(normalizedChanges); + + // Add in a text edit between the last actual copilot edit and the first close brace edit we're adding. That way + // we ensure that the code in between those sections is properly formatted (similar to if the user had + // explicitly typed the close brace themselves. + if (lastTextChange.Span.End < insertCloseBraceTextChanges.First().Span.Start) + { + var interstitialSpan = TextSpan.FromBounds(lastTextChange.Span.End, insertCloseBraceTextChanges.First().Span.Start); + builder.Add(new TextChange(interstitialSpan, originalText.ToString(interstitialSpan))); + } + + builder.AddRange(insertCloseBraceTextChanges); + + var finalTextChanges = builder.ToImmutableAndClear(); + return finalTextChanges; + } +} diff --git a/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 6001081d453..ba512044830 100644 --- a/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/roslyn/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -1665,7 +1665,7 @@ public static IEnumerable GetSequenceEdits(ImmutableArray { - internal static readonly LcsTokens Instance = new LcsTokens(); + internal static readonly LcsTokens Instance = new(); protected override bool Equals(SyntaxToken oldElement, SyntaxToken newElement) => SyntaxFactory.AreEquivalent(oldElement, newElement); @@ -1673,7 +1673,7 @@ protected override bool Equals(SyntaxToken oldElement, SyntaxToken newElement) private sealed class LcsNodes : LongestCommonImmutableArraySubsequence { - internal static readonly LcsNodes Instance = new LcsNodes(); + internal static readonly LcsNodes Instance = new(); protected override bool Equals(SyntaxNode oldElement, SyntaxNode newElement) => SyntaxFactory.AreEquivalent(oldElement, newElement); diff --git a/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs b/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs index 13a953ca4de..f2b2669799a 100644 --- a/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs +++ b/src/roslyn/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs @@ -130,7 +130,7 @@ protected override async Task> GetFieldsAsync(Docum .OfType() .Where(n => n.Span.IntersectsWith(span)); - var declarations = fields.Where(CanEncapsulate).Select(f => f.Declaration); + var declarations = fields.SelectAsArray(CanEncapsulate, f => f.Declaration); IEnumerable declarators; if (span.IsEmpty) @@ -144,9 +144,10 @@ protected override async Task> GetFieldsAsync(Docum declarators = declarations.SelectMany(d => d.Variables.Where(v => v.Span.IntersectsWith(span))); } - return [.. declarators.Select(d => semanticModel.GetDeclaredSymbol(d, cancellationToken) as IFieldSymbol) - .WhereNotNull() - .Where(f => f.Name.Length != 0)]; + return [.. declarators + .Select(d => semanticModel.GetDeclaredSymbol(d, cancellationToken) as IFieldSymbol) + .WhereNotNull() + .Where(f => f.Name.Length != 0)]; } private bool CanEncapsulate(FieldDeclarationSyntax field) diff --git a/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs b/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs index deb42a0126c..cc50e0915d4 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ExternalAccess/Pythia/Api/PythiaSignatureHelpItemWrapper.cs @@ -13,7 +13,7 @@ internal readonly struct PythiaSignatureHelpItemWrapper(SignatureHelpItem underl internal readonly SignatureHelpItem UnderlyingObject = underlyingObject; public static SymbolDisplayPart CreateTextDisplayPart(string text) - => new SymbolDisplayPart(SymbolDisplayPartKind.Text, null, text); + => new(SymbolDisplayPartKind.Text, null, text); public static PythiaSignatureHelpItemWrapper CreateFromMethodGroupMethod( Document document, @@ -21,5 +21,5 @@ public static PythiaSignatureHelpItemWrapper CreateFromMethodGroupMethod( int position, SemanticModel semanticModel, IList descriptionParts) - => new PythiaSignatureHelpItemWrapper(AbstractOrdinaryMethodSignatureHelpProvider.ConvertMethodGroupMethod(document, method, position, semanticModel, descriptionParts)); + => new(AbstractOrdinaryMethodSignatureHelpProvider.ConvertMethodGroupMethod(document, method, position, semanticModel, [.. descriptionParts])); } diff --git a/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs b/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs index 6da63b6307e..ecd8a5ff555 100644 --- a/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs +++ b/src/roslyn/src/Features/CSharp/Portable/ExtractMethod/CSharpSyntaxTriviaService.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; internal sealed class CSharpSyntaxTriviaService : AbstractSyntaxTriviaService { - public static readonly CSharpSyntaxTriviaService Instance = new CSharpSyntaxTriviaService(); + public static readonly CSharpSyntaxTriviaService Instance = new(); private CSharpSyntaxTriviaService() : base((int)SyntaxKind.EndOfLineTrivia) diff --git a/src/roslyn/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs b/src/roslyn/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs index 5ee7dda238b..7a9ee0dbc55 100644 --- a/src/roslyn/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs +++ b/src/roslyn/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs @@ -322,7 +322,7 @@ scope is ICompilationUnitSyntax foreach (var match in matches) editor.ReplaceNode(match, replacement); - if (scope is BlockSyntax block) + if (matches.FindInnermostCommonBlock() is BlockSyntax block) { var firstAffectedStatement = block.Statements.Single(s => firstAffectedExpression.GetAncestorOrThis()!.Contains(s)); var firstAffectedStatementIndex = block.Statements.IndexOf(firstAffectedStatement); diff --git a/src/roslyn/src/Features/CSharp/Portable/InvertConditional/CSharpInvertConditionalCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/InvertConditional/CSharpInvertConditionalCodeRefactoringProvider.cs index 0cff430aba8..152d83e28a6 100644 --- a/src/roslyn/src/Features/CSharp/Portable/InvertConditional/CSharpInvertConditionalCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/InvertConditional/CSharpInvertConditionalCodeRefactoringProvider.cs @@ -14,14 +14,11 @@ namespace Microsoft.CodeAnalysis.CSharp.InvertConditional; [ExtensionOrder(Before = PredefinedCodeRefactoringProviderNames.IntroduceVariable)] [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InvertConditional), Shared] -internal sealed class CSharpInvertConditionalCodeRefactoringProvider +[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 CSharpInvertConditionalCodeRefactoringProvider() : AbstractInvertConditionalCodeRefactoringProvider { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpInvertConditionalCodeRefactoringProvider() - { - } // Don't offer if the conditional is missing the colon and the conditional is too incomplete. protected override bool ShouldOffer(ConditionalExpressionSyntax conditional) diff --git a/src/roslyn/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs index 83eb0d70ac6..51f5524bb1e 100644 --- a/src/roslyn/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs @@ -25,7 +25,12 @@ namespace Microsoft.CodeAnalysis.CSharp.InvertIf; [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class CSharpInvertIfCodeRefactoringProvider() : AbstractInvertIfCodeRefactoringProvider< - SyntaxKind, StatementSyntax, IfStatementSyntax, StatementSyntax> + SyntaxKind, + StatementSyntax, + IfStatementSyntax, + StatementSyntax, + DirectiveTriviaSyntax, + IfDirectiveTriviaSyntax> { protected override string GetTitle() => CSharpFeaturesResources.Invert_if; @@ -39,6 +44,9 @@ protected override bool CanInvert(IfStatementSyntax ifNode) protected override SyntaxNode GetCondition(IfStatementSyntax ifNode) => ifNode.Condition; + protected override SyntaxNode GetCondition(IfDirectiveTriviaSyntax ifNode) + => ifNode.Condition; + protected override StatementRange GetIfBodyStatementRange(IfStatementSyntax ifNode) => new(ifNode.Statement, ifNode.Statement); diff --git a/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs b/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs index 52486fc2ec5..c13f4d3f7e2 100644 --- a/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/QuickInfo/CSharpDiagnosticAnalyzerQuickInfoProvider.cs @@ -25,17 +25,17 @@ namespace Microsoft.CodeAnalysis.CSharp.QuickInfo; [ExtensionOrder(Before = QuickInfoProviderNames.Semantic)] [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 CSharpDiagnosticAnalyzerQuickInfoProvider(DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache) : CommonQuickInfoProvider +internal sealed class CSharpDiagnosticAnalyzerQuickInfoProvider() : CommonQuickInfoProvider { - private readonly DiagnosticAnalyzerInfoCache _diagnosticAnalyzerInfoCache = globalCache.AnalyzerInfoCache; - protected override async Task BuildQuickInfoAsync( QuickInfoContext context, SyntaxToken token) { var document = context.Document; - return GetQuickinfoForPragmaWarning(document, token) ?? - (await GetQuickInfoForSuppressMessageAttributeAsync(document, token, context.CancellationToken).ConfigureAwait(false)); + var cancellationToken = context.CancellationToken; + return + await GetQuickinfoForPragmaWarningAsync(document, token, cancellationToken).ConfigureAwait(false) ?? + await GetQuickInfoForSuppressMessageAttributeAsync(document, token, cancellationToken).ConfigureAwait(false); } protected override Task BuildQuickInfoAsync( @@ -47,7 +47,8 @@ internal sealed class CSharpDiagnosticAnalyzerQuickInfoProvider(DiagnosticAnalyz return Task.FromResult(null); } - private QuickInfoItem? GetQuickinfoForPragmaWarning(Document document, SyntaxToken token) + private static async Task GetQuickinfoForPragmaWarningAsync( + Document document, SyntaxToken token, CancellationToken cancellationToken) { var errorCodeNode = token.Parent switch { @@ -83,10 +84,11 @@ PragmaWarningDirectiveTriviaSyntax directive return null; } - return GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzers(document, errorCode, errorCodeNode.Span); + return await GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzersAsync( + document, errorCode, errorCodeNode.Span, cancellationToken).ConfigureAwait(false); } - private async Task GetQuickInfoForSuppressMessageAttributeAsync( + private static async Task GetQuickInfoForSuppressMessageAttributeAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) @@ -121,19 +123,22 @@ PragmaWarningDirectiveTriviaSyntax directive if (checkIdObject.HasValue && checkIdObject.Value is string checkId) { var errorCode = checkId.ExtractErrorCodeFromCheckId(); - return GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzers(document, errorCode, suppressMessageCheckIdArgument.Span); + return await GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzersAsync( + document, errorCode, suppressMessageCheckIdArgument.Span, cancellationToken).ConfigureAwait(false); } } return null; } - private QuickInfoItem? GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzers(Document document, - string errorCode, TextSpan location) + private static async Task GetQuickInfoFromSupportedDiagnosticsOfProjectAnalyzersAsync( + Document document, string errorCode, TextSpan location, CancellationToken cancellationToken) { var hostAnalyzers = document.Project.Solution.SolutionState.Analyzers; - var groupedDiagnostics = hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache, document.Project).Values; - var supportedDiagnostics = groupedDiagnostics.SelectMany(d => d); + var service = document.Project.Solution.Services.GetRequiredService(); + var groupedDiagnostics = await service.GetDiagnosticDescriptorsPerReferenceAsync( + document.Project, cancellationToken).ConfigureAwait(false); + var supportedDiagnostics = groupedDiagnostics.Values.SelectMany(d => d); var diagnosticDescriptor = supportedDiagnostics.FirstOrDefault(d => d.Id == errorCode); if (diagnosticDescriptor != null) { diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs index 3b4adc4a334..d6ce004565d 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractCSharpSignatureHelpProvider.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; @@ -16,30 +17,30 @@ internal abstract partial class AbstractCSharpSignatureHelpProvider : AbstractSi .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.AllowDefaultLiteral); protected static SymbolDisplayPart Keyword(SyntaxKind kind) - => new SymbolDisplayPart(SymbolDisplayPartKind.Keyword, null, SyntaxFacts.GetText(kind)); + => new(SymbolDisplayPartKind.Keyword, null, SyntaxFacts.GetText(kind)); protected static SymbolDisplayPart Operator(SyntaxKind kind) - => new SymbolDisplayPart(SymbolDisplayPartKind.Operator, null, SyntaxFacts.GetText(kind)); + => new(SymbolDisplayPartKind.Operator, null, SyntaxFacts.GetText(kind)); protected static SymbolDisplayPart Punctuation(SyntaxKind kind) - => new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, SyntaxFacts.GetText(kind)); + => new(SymbolDisplayPartKind.Punctuation, null, SyntaxFacts.GetText(kind)); protected static SymbolDisplayPart Text(string text) - => new SymbolDisplayPart(SymbolDisplayPartKind.Text, null, text); + => new(SymbolDisplayPartKind.Text, null, text); protected static SymbolDisplayPart Space() - => new SymbolDisplayPart(SymbolDisplayPartKind.Space, null, " "); + => new(SymbolDisplayPartKind.Space, null, " "); protected static SymbolDisplayPart NewLine() - => new SymbolDisplayPart(SymbolDisplayPartKind.LineBreak, null, "\r\n"); + => new(SymbolDisplayPartKind.LineBreak, null, "\r\n"); - private static readonly IList _separatorParts = + private static readonly ImmutableArray _separatorParts = [ Punctuation(SyntaxKind.CommaToken), Space() ]; - protected static IList GetSeparatorParts() => _separatorParts; + protected static ImmutableArray GetSeparatorParts() => _separatorParts; protected static SignatureHelpSymbolParameter Convert( IParameterSymbol parameter, diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider.cs similarity index 77% rename from src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs rename to src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider.cs index 2a478ebc057..095d9618104 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,64 +12,29 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.DocumentationComments; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; -[ExportSignatureHelpProvider("GenericNameSignatureHelpProvider", LanguageNames.CSharp), Shared] -internal partial class GenericNameSignatureHelpProvider : AbstractCSharpSignatureHelpProvider +internal abstract partial class AbstractGenericNameSignatureHelpProvider : AbstractCSharpSignatureHelpProvider { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public GenericNameSignatureHelpProvider() - { - } - public override ImmutableArray TriggerCharacters => ['<', ',']; public override ImmutableArray RetriggerCharacters => ['>']; - protected virtual bool TryGetGenericIdentifier( + protected abstract TextSpan GetTextSpan(SyntaxToken genericIdentifier, SyntaxToken lessThanToken); + + protected abstract bool TryGetGenericIdentifier( SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, SignatureHelpTriggerReason triggerReason, CancellationToken cancellationToken, out SyntaxToken genericIdentifier, - out SyntaxToken lessThanToken) - { - if (CommonSignatureHelpUtilities.TryGetSyntax( - root, position, syntaxFacts, triggerReason, IsTriggerToken, IsArgumentListToken, cancellationToken, out GenericNameSyntax? name)) - { - genericIdentifier = name.Identifier; - lessThanToken = name.TypeArgumentList.LessThanToken; - return true; - } - - genericIdentifier = default; - lessThanToken = default; - return false; - } - - private bool IsTriggerToken(SyntaxToken token) - { - return !token.IsKind(SyntaxKind.None) && - token.ValueText.Length == 1 && - TriggerCharacters.Contains(token.ValueText[0]) && - token.Parent is TypeArgumentListSyntax && - token.Parent.Parent is GenericNameSyntax; - } - - private bool IsArgumentListToken(GenericNameSyntax node, SyntaxToken token) - { - return node.TypeArgumentList != null && - node.TypeArgumentList.Span.Contains(token.SpanStart) && - token != node.TypeArgumentList.GreaterThanToken; - } + out SyntaxToken lessThanToken); protected override async Task GetItemsWorkerAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, MemberDisplayOptions options, CancellationToken cancellationToken) { @@ -116,16 +79,13 @@ private bool IsArgumentListToken(GenericNameSyntax node, SyntaxToken token) return null; } - var accessibleSymbols = - symbols.WhereAsArray(s => s.GetArity() > 0) - .WhereAsArray(s => s is INamedTypeSymbol or IMethodSymbol) - .FilterToVisibleAndBrowsableSymbols(options.HideAdvancedMembers, semanticModel.Compilation, inclusionFilter: static s => true) - .Sort(semanticModel, genericIdentifier.SpanStart); + var accessibleSymbols = symbols + .WhereAsArray(s => s.GetArity() > 0) + .FilterToVisibleAndBrowsableSymbols(options.HideAdvancedMembers, semanticModel.Compilation, inclusionFilter: static s => true) + .Sort(semanticModel, genericIdentifier.SpanStart); if (!accessibleSymbols.Any()) - { return null; - } var structuralTypeDisplayService = document.GetRequiredLanguageService(); var documentationCommentFormattingService = document.GetRequiredLanguageService(); @@ -158,12 +118,6 @@ private bool IsArgumentListToken(GenericNameSyntax node, SyntaxToken token) return null; } - protected virtual TextSpan GetTextSpan(SyntaxToken genericIdentifier, SyntaxToken lessThanToken) - { - Contract.ThrowIfFalse(lessThanToken.Parent is TypeArgumentListSyntax && lessThanToken.Parent.Parent is GenericNameSyntax); - return SignatureHelpUtilities.GetSignatureHelpSpan(((GenericNameSyntax)lessThanToken.Parent.Parent).TypeArgumentList); - } - private static SignatureHelpItem Convert( ISymbol symbol, SyntaxToken lessThanToken, @@ -173,34 +127,54 @@ private static SignatureHelpItem Convert( { var position = lessThanToken.SpanStart; - SignatureHelpItem item; if (symbol is INamedTypeSymbol namedType) { - item = CreateItem( + return CreateItem( symbol, semanticModel, position, structuralTypeDisplayService, - false, + isVariadic: false, symbol.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), GetPreambleParts(namedType, semanticModel, position), GetSeparatorParts(), GetPostambleParts(), [.. namedType.TypeParameters.Select(p => Convert(p, semanticModel, position, documentationCommentFormattingService))]); } - else + else if (symbol is IMethodSymbol method) { - var method = (IMethodSymbol)symbol; - item = CreateItem( + return CreateItem( symbol, semanticModel, position, structuralTypeDisplayService, - false, - c => symbol.GetDocumentationParts(semanticModel, position, documentationCommentFormattingService, c), + isVariadic: false, + symbol.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), GetPreambleParts(method, semanticModel, position), GetSeparatorParts(), GetPostambleParts(method, semanticModel, position), - [.. method.TypeParameters.Select(p => Convert(p, semanticModel, position, documentationCommentFormattingService))]); + GetTypeArguments(method)); + } + else + { + throw ExceptionUtilities.UnexpectedValue(symbol); } - return item; + ImmutableArray GetTypeArguments(IMethodSymbol method) + { + using var _ = ArrayBuilder.GetInstance(out var result); + + // Signature help for generic modern extensions must include the generic type *arguments* for the containing + // extension as well. These are fixed given the receiver, and need to be repeated in the method type argument + // list. + if (method.ContainingType.IsExtension) + { + result.AddRange(method.ContainingType.TypeArguments.Select(t => new SignatureHelpSymbolParameter( + name: null, isOptional: false, + t.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), + t.ToMinimalDisplayParts(semanticModel, position)))); + } + + result.AddRange(method.TypeParameters.Select(p => Convert(p, semanticModel, position, documentationCommentFormattingService))); + + return result.ToImmutableAndClear(); + } } private static readonly SymbolDisplayFormat s_minimallyQualifiedFormat = @@ -221,12 +195,12 @@ private static SignatureHelpSymbolParameter Convert( selectedDisplayParts: GetSelectedDisplayParts(parameter, semanticModel, position)); } - private static IList GetSelectedDisplayParts( + private static ImmutableArray GetSelectedDisplayParts( ITypeParameterSymbol typeParam, SemanticModel semanticModel, int position) { - var parts = new List(); + using var _ = ArrayBuilder.GetInstance(out var parts); if (TypeParameterHasConstraints(typeParam)) { @@ -302,7 +276,7 @@ private static IList GetSelectedDisplayParts( } } - return parts; + return parts.ToImmutableAndClear(); } private static bool TypeParameterHasConstraints(ITypeParameterSymbol typeParam) diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider_Method.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider_Method.cs similarity index 79% rename from src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider_Method.cs rename to src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider_Method.cs index c1072cfa05d..79fce86ad22 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider_Method.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider_Method.cs @@ -2,19 +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.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; -internal partial class GenericNameSignatureHelpProvider +internal partial class AbstractGenericNameSignatureHelpProvider { - private static IList GetPreambleParts( + private static ImmutableArray GetPreambleParts( IMethodSymbol method, SemanticModel semanticModel, int position) { - var result = new List(); + using var _ = ArrayBuilder.GetInstance(out var result); var awaitable = method.GetOriginalUnreducedDefinition().IsAwaitableNonDynamic(semanticModel, position); var extension = method.GetOriginalUnreducedDefinition().IsExtensionMethod(); @@ -55,7 +56,7 @@ private static IList GetPreambleParts( result.Add(new SymbolDisplayPart(SymbolDisplayPartKind.MethodName, method, method.Name)); result.Add(Punctuation(SyntaxKind.LessThanToken)); - return result; + return result.ToImmutableAndClear(); } private static ITypeSymbol? GetContainingType(IMethodSymbol method) @@ -71,13 +72,11 @@ private static IList GetPreambleParts( } } - private static IList GetPostambleParts(IMethodSymbol method, SemanticModel semanticModel, int position) + private static ImmutableArray GetPostambleParts(IMethodSymbol method, SemanticModel semanticModel, int position) { - var result = new List - { - Punctuation(SyntaxKind.GreaterThanToken), - Punctuation(SyntaxKind.OpenParenToken) - }; + using var _ = ArrayBuilder.GetInstance(out var result); + result.Add(Punctuation(SyntaxKind.GreaterThanToken)); + result.Add(Punctuation(SyntaxKind.OpenParenToken)); var first = true; foreach (var parameter in method.Parameters) @@ -93,6 +92,6 @@ private static IList GetPostambleParts(IMethodSymbol method, } result.Add(Punctuation(SyntaxKind.CloseParenToken)); - return result; + return result.ToImmutableAndClear(); } } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider_NamedType.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider_NamedType.cs similarity index 70% rename from src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider_NamedType.cs rename to src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider_NamedType.cs index d3546f93092..d90fa2f98c2 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider_NamedType.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractGenericNameSignatureHelpProvider_NamedType.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.Collections.Generic; +using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; -internal partial class GenericNameSignatureHelpProvider +internal partial class AbstractGenericNameSignatureHelpProvider { - private static IList GetPreambleParts( + private static ImmutableArray GetPreambleParts( INamedTypeSymbol namedType, SemanticModel semanticModel, int position) @@ -16,6 +16,6 @@ private static IList GetPreambleParts( return [.. namedType.ToMinimalDisplayParts(semanticModel, position, MinimallyQualifiedWithoutTypeParametersFormat), Punctuation(SyntaxKind.LessThanToken)]; } - private static IList GetPostambleParts() + private static ImmutableArray GetPostambleParts() => [Punctuation(SyntaxKind.GreaterThanToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.cs index 290c2cc2d1d..de801fca0b0 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.cs @@ -2,10 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; @@ -27,7 +28,7 @@ internal static SignatureHelpItem ConvertMethodGroupMethod( IMethodSymbol method, int position, SemanticModel semanticModel, - IList? descriptionParts) + ImmutableArray? descriptionParts) { var structuralTypeDisplayService = document.GetRequiredLanguageService(); var documentationCommentFormattingService = document.GetRequiredLanguageService(); @@ -44,12 +45,12 @@ [.. method.Parameters.Select(p => Convert(p, semanticModel, position, documentat descriptionParts: descriptionParts); } - private static IList GetMethodGroupPreambleParts( + private static ImmutableArray GetMethodGroupPreambleParts( IMethodSymbol method, SemanticModel semanticModel, int position) { - var result = new List(); + using var _ = ArrayBuilder.GetInstance(out var result); var awaitable = method.GetOriginalUnreducedDefinition().IsAwaitableNonDynamic(semanticModel, position); var extension = method.GetOriginalUnreducedDefinition().IsExtensionMethod(); @@ -81,9 +82,9 @@ private static IList GetMethodGroupPreambleParts( result.AddRange(method.ToMinimalDisplayParts(semanticModel, position, MinimallyQualifiedWithoutParametersFormat)); result.Add(Punctuation(SyntaxKind.OpenParenToken)); - return result; + return result.ToImmutableAndClear(); } - private static IList GetMethodGroupPostambleParts() + private static ImmutableArray GetMethodGroupPostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs index a84a014f940..30c34711fd5 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.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.Composition; using System.Diagnostics.CodeAnalysis; @@ -16,6 +15,7 @@ using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.CodeAnalysis.Text; @@ -135,10 +135,10 @@ private static SignatureHelpItem Convert( var position = attribute.SpanStart; var namedParameters = constructor.ContainingType.GetAttributeNamedParameters(semanticModel.Compilation, within) .OrderBy(s => s.Name) - .ToList(); + .ToImmutableArray(); var isVariadic = - constructor.Parameters is [.., { IsParams: true }] && namedParameters.Count == 0; + constructor.Parameters is [.., { IsParams: true }] && namedParameters.IsEmpty; var item = CreateItem( constructor, semanticModel, position, @@ -152,51 +152,45 @@ private static SignatureHelpItem Convert( return item; } - private static IList GetParameters( + private static ImmutableArray GetParameters( IMethodSymbol constructor, SemanticModel semanticModel, int position, - IList namedParameters, + ImmutableArray namedParameters, IDocumentationCommentFormattingService documentationCommentFormatter, CancellationToken cancellationToken) { - var result = new List(); + using var _ = ArrayBuilder.GetInstance(out var result); foreach (var parameter in constructor.Parameters) - { result.Add(Convert(parameter, semanticModel, position, documentationCommentFormatter)); - } - for (var i = 0; i < namedParameters.Count; i++) + for (var i = 0; i < namedParameters.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); var namedParameter = namedParameters[i]; - var type = namedParameter is IFieldSymbol ? ((IFieldSymbol)namedParameter).Type : ((IPropertySymbol)namedParameter).Type; - - var displayParts = new List - { - new SymbolDisplayPart( - namedParameter is IFieldSymbol ? SymbolDisplayPartKind.FieldName : SymbolDisplayPartKind.PropertyName, - namedParameter, namedParameter.Name.ToIdentifierToken().ToString()), - Space(), - Punctuation(SyntaxKind.EqualsToken), - Space() - }; - displayParts.AddRange(type.ToMinimalDisplayParts(semanticModel, position)); - + var type = namedParameter is IFieldSymbol field ? field.Type : ((IPropertySymbol)namedParameter).Type; result.Add(new SignatureHelpSymbolParameter( namedParameter.Name, isOptional: true, documentationFactory: namedParameter.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormatter), - displayParts: displayParts, + displayParts: + [ + new(namedParameter is IFieldSymbol ? SymbolDisplayPartKind.FieldName : SymbolDisplayPartKind.PropertyName, + namedParameter, namedParameter.Name.ToIdentifierToken().ToString()), + Space(), + Punctuation(SyntaxKind.EqualsToken), + Space(), + .. type.ToMinimalDisplayParts(semanticModel, position), + ], prefixDisplayParts: GetParameterPrefixDisplayParts(i))); } - return result; + return result.ToImmutableAndClear(); } - private static List? GetParameterPrefixDisplayParts(int i) + private static ImmutableArray? GetParameterPrefixDisplayParts(int i) { if (i == 0) { @@ -211,7 +205,7 @@ private static IList GetParameters( return null; } - private static IList GetPreambleParts( + private static ImmutableArray GetPreambleParts( IMethodSymbol method, SemanticModel semanticModel, int position) @@ -219,6 +213,6 @@ private static IList GetPreambleParts( return [.. method.ContainingType.ToMinimalDisplayParts(semanticModel, position), Punctuation(SyntaxKind.OpenParenToken)]; } - private static IList GetPostambleParts() + private static ImmutableArray GetPostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ConstructorInitializerSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ConstructorInitializerSignatureHelpProvider.cs index 9e87eba563f..06576d60e5d 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ConstructorInitializerSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ConstructorInitializerSignatureHelpProvider.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.Composition; using System.Linq; @@ -148,7 +147,7 @@ private static SignatureHelpItem Convert( return item; } - private static IList GetPreambleParts( + private static ImmutableArray GetPreambleParts( IMethodSymbol method, SemanticModel semanticModel, int position) @@ -156,6 +155,6 @@ private static IList GetPreambleParts( return [.. method.ContainingType.ToMinimalDisplayParts(semanticModel, position), Punctuation(SyntaxKind.OpenParenToken)]; } - private static IList GetPostambleParts() + private static ImmutableArray GetPostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs index 16b7815d93d..4480f8fcad5 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.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.Composition; using System.Diagnostics.CodeAnalysis; @@ -16,6 +15,7 @@ using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.CodeAnalysis.Text; @@ -234,12 +234,12 @@ private static SignatureHelpItem Convert( return item; } - private static IList GetPreambleParts( + private static ImmutableArray GetPreambleParts( IPropertySymbol indexer, int position, SemanticModel semanticModel) { - var result = new List(); + using var _ = ArrayBuilder.GetInstance(out var result); if (indexer.ReturnsByRef) { @@ -266,10 +266,10 @@ private static IList GetPreambleParts( result.Add(Punctuation(SyntaxKind.OpenBracketToken)); - return result; + return result.ToImmutableAndClear(); } - private static IList GetPostambleParts() + private static ImmutableArray GetPostambleParts() => [Punctuation(SyntaxKind.CloseBracketToken)]; private static class CompleteElementAccessExpression diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameFullyWrittenSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameFullyWrittenSignatureHelpProvider.cs new file mode 100644 index 00000000000..f6a66e0567f --- /dev/null +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNameFullyWrittenSignatureHelpProvider.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.SignatureHelp; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; + +[ExportSignatureHelpProvider(nameof(GenericNameFullyWrittenSignatureHelpProvider), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class GenericNameFullyWrittenSignatureHelpProvider() : AbstractGenericNameSignatureHelpProvider +{ + protected override bool TryGetGenericIdentifier( + SyntaxNode root, int position, + ISyntaxFactsService syntaxFacts, + SignatureHelpTriggerReason triggerReason, + CancellationToken cancellationToken, + out SyntaxToken genericIdentifier, + out SyntaxToken lessThanToken) + { + if (CommonSignatureHelpUtilities.TryGetSyntax( + root, position, syntaxFacts, triggerReason, IsTriggerToken, IsArgumentListToken, cancellationToken, out GenericNameSyntax? name)) + { + genericIdentifier = name.Identifier; + lessThanToken = name.TypeArgumentList.LessThanToken; + return true; + } + + genericIdentifier = default; + lessThanToken = default; + return false; + } + + private bool IsTriggerToken(SyntaxToken token) + { + return !token.IsKind(SyntaxKind.None) && + token.ValueText.Length == 1 && + TriggerCharacters.Contains(token.ValueText[0]) && + token.Parent is TypeArgumentListSyntax && + token.Parent.Parent is GenericNameSyntax; + } + + private bool IsArgumentListToken(GenericNameSyntax node, SyntaxToken token) + { + return node.TypeArgumentList != null && + node.TypeArgumentList.Span.Contains(token.SpanStart) && + token != node.TypeArgumentList.GreaterThanToken; + } + + protected override TextSpan GetTextSpan(SyntaxToken genericIdentifier, SyntaxToken lessThanToken) + { + Contract.ThrowIfFalse(lessThanToken.Parent is TypeArgumentListSyntax && lessThanToken.Parent.Parent is GenericNameSyntax); + return SignatureHelpUtilities.GetSignatureHelpSpan(((GenericNameSyntax)lessThanToken.Parent.Parent).TypeArgumentList); + } +} diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs index 6a73d1fe258..a20e59f15dc 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs @@ -13,15 +13,11 @@ namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; -[ExportSignatureHelpProvider("GenericNamePartiallyWrittenSignatureHelpProvider", LanguageNames.CSharp), Shared] -internal sealed class GenericNamePartiallyWrittenSignatureHelpProvider : GenericNameSignatureHelpProvider +[ExportSignatureHelpProvider(nameof(GenericNamePartiallyWrittenSignatureHelpProvider), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class GenericNamePartiallyWrittenSignatureHelpProvider() : AbstractGenericNameSignatureHelpProvider { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public GenericNamePartiallyWrittenSignatureHelpProvider() - { - } - protected override bool TryGetGenericIdentifier(SyntaxNode root, int position, ISyntaxFactsService syntaxFacts, SignatureHelpTriggerReason triggerReason, CancellationToken cancellationToken, out SyntaxToken genericIdentifier, out SyntaxToken lessThanToken) => root.SyntaxTree.IsInPartiallyWrittenGeneric(position, cancellationToken, out genericIdentifier, out lessThanToken); diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProviderBase_DelegateAndFunctionPointerInvoke.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProviderBase_DelegateAndFunctionPointerInvoke.cs index 5b6dcce2a53..9b2b5143924 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProviderBase_DelegateAndFunctionPointerInvoke.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProviderBase_DelegateAndFunctionPointerInvoke.cs @@ -2,12 +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.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; @@ -36,7 +37,7 @@ internal abstract partial class InvocationExpressionSignatureHelpProviderBase return invokeMethod; } - private static IList GetDelegateOrFunctionPointerInvokeItems(InvocationExpressionSyntax invocationExpression, IMethodSymbol invokeMethod, SemanticModel semanticModel, IStructuralTypeDisplayService structuralTypeDisplayService, IDocumentationCommentFormattingService documentationCommentFormattingService, out int? selectedItem, CancellationToken cancellationToken) + private static ImmutableArray GetDelegateOrFunctionPointerInvokeItems(InvocationExpressionSyntax invocationExpression, IMethodSymbol invokeMethod, SemanticModel semanticModel, IStructuralTypeDisplayService structuralTypeDisplayService, IDocumentationCommentFormattingService documentationCommentFormattingService, out int? selectedItem, CancellationToken cancellationToken) { var position = invocationExpression.SpanStart; var item = CreateItem( @@ -55,9 +56,9 @@ private static IList GetDelegateOrFunctionPointerInvokeItems( return [item]; } - private static IList GetDelegateOrFunctionPointerInvokePreambleParts(IMethodSymbol invokeMethod, SemanticModel semanticModel, int position) + private static ImmutableArray GetDelegateOrFunctionPointerInvokePreambleParts(IMethodSymbol invokeMethod, SemanticModel semanticModel, int position) { - var displayParts = new List(); + using var _ = ArrayBuilder.GetInstance(out var displayParts); displayParts.AddRange(invokeMethod.ReturnType.ToMinimalDisplayParts(semanticModel, position)); displayParts.Add(Space()); @@ -73,13 +74,13 @@ private static IList GetDelegateOrFunctionPointerInvokePreamb displayParts.Add(Punctuation(SyntaxKind.OpenParenToken)); - return displayParts; + return displayParts.ToImmutableAndClear(); } - private static IList GetDelegateOrFunctionPointerInvokeParameters( + private static ImmutableArray GetDelegateOrFunctionPointerInvokeParameters( IMethodSymbol invokeMethod, SemanticModel semanticModel, int position, IDocumentationCommentFormattingService formattingService, CancellationToken cancellationToken) { - var result = new List(); + using var _ = ArrayBuilder.GetInstance(out var result); foreach (var parameter in invokeMethod.Parameters) { @@ -91,9 +92,9 @@ private static IList GetDelegateOrFunctionPointerI parameter.ToMinimalDisplayParts(semanticModel, position))); } - return result; + return result.ToImmutableAndClear(); } - private static IList GetDelegateOrFunctionPointerInvokePostambleParts() + private static ImmutableArray GetDelegateOrFunctionPointerInvokePostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_DelegateType.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_DelegateType.cs index b25747835e5..e46a14aafe5 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_DelegateType.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_DelegateType.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.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.SignatureHelp; namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; @@ -33,21 +33,17 @@ private static ImmutableArray ConvertDelegateTypeConstructor( return [item]; } - private static IList GetDelegateTypePreambleParts(IMethodSymbol invokeMethod, SemanticModel semanticModel, int position) - { - var result = new List(); - - result.AddRange(invokeMethod.ContainingType.ToMinimalDisplayParts(semanticModel, position)); - result.Add(Punctuation(SyntaxKind.OpenParenToken)); - - return result; - } + private static ImmutableArray GetDelegateTypePreambleParts(IMethodSymbol invokeMethod, SemanticModel semanticModel, int position) + => [ + .. invokeMethod.ContainingType.ToMinimalDisplayParts(semanticModel, position), + Punctuation(SyntaxKind.OpenParenToken), + ]; - private static IList GetDelegateTypeParameters(IMethodSymbol invokeMethod, SemanticModel semanticModel, int position) + private static ImmutableArray GetDelegateTypeParameters(IMethodSymbol invokeMethod, SemanticModel semanticModel, int position) { const string TargetName = "target"; - var parts = new List(); + using var _ = ArrayBuilder.GetInstance(out var parts); parts.AddRange(invokeMethod.ReturnType.ToMinimalDisplayParts(semanticModel, position)); parts.Add(Space()); parts.Add(Punctuation(SyntaxKind.OpenParenToken)); @@ -73,9 +69,9 @@ private static IList GetDelegateTypeParameters(IMe TargetName, isOptional: false, documentationFactory: null, - displayParts: parts)]; + displayParts: parts.ToImmutableAndClear())]; } - private static IList GetDelegateTypePostambleParts() + private static ImmutableArray GetDelegateTypePostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_NormalType.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_NormalType.cs index 01e6979432a..c93262a9f22 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_NormalType.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider_NormalType.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.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -36,19 +36,13 @@ private static SignatureHelpItem ConvertNormalTypeConstructor( return item; } - private static IList GetNormalTypePreambleParts( - IMethodSymbol method, - SemanticModel semanticModel, - int position) - { - var result = new List(); - - result.AddRange(method.ContainingType.ToMinimalDisplayParts(semanticModel, position)); - result.Add(Punctuation(SyntaxKind.OpenParenToken)); - - return result; - } + private static ImmutableArray GetNormalTypePreambleParts( + IMethodSymbol method, SemanticModel semanticModel, int position) + => [ + .. method.ContainingType.ToMinimalDisplayParts(semanticModel, position), + Punctuation(SyntaxKind.OpenParenToken), + ]; - private static IList GetNormalTypePostambleParts() + private static ImmutableArray GetNormalTypePostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProvider.cs index a836df057d0..3e61af4945b 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProvider.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.Composition; using System.Diagnostics.CodeAnalysis; @@ -128,26 +127,16 @@ private static SignatureHelpItem Convert( structuralTypeDisplayService, constructor.IsParams(), constructor.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), - GetPreambleParts(constructor, semanticModel, position), + GetPreambleParts(), GetSeparatorParts(), GetPostambleParts(), [.. constructor.Parameters.Select(p => Convert(p, semanticModel, position, documentationCommentFormattingService))]); return item; - static IList GetPreambleParts( - IMethodSymbol method, - SemanticModel semanticModel, - int position) - { - var result = new List(); - - result.AddRange(method.ContainingType.ToMinimalDisplayParts(semanticModel, position)); - result.Add(Punctuation(SyntaxKind.OpenParenToken)); - - return result; - } + ImmutableArray GetPreambleParts() + => [.. constructor.ContainingType.ToMinimalDisplayParts(semanticModel, position), Punctuation(SyntaxKind.OpenParenToken)]; - static IList GetPostambleParts() + static ImmutableArray GetPostambleParts() => [Punctuation(SyntaxKind.CloseParenToken)]; } } diff --git a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/TupleConstructionSignatureHelpProvider.cs b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/TupleConstructionSignatureHelpProvider.cs index 9b63703f76e..f5aef35c99c 100644 --- a/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/TupleConstructionSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/SignatureHelp/TupleConstructionSignatureHelpProvider.cs @@ -153,9 +153,8 @@ private bool GetOuterMostParenthesizedExpressionInSpan(SyntaxNode root, int posi var suffixParts = SpecializedCollections.SingletonEnumerable(new SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, null, ")")).ToTaggedText(); var separatorParts = GetSeparatorParts().ToTaggedText(); - var items = tupleTypes.Select(tupleType => Convert( - tupleType, prefixParts, suffixParts, separatorParts, semanticModel, position)) - .ToList(); + var items = tupleTypes.SelectAsArray(tupleType => Convert( + tupleType, prefixParts, suffixParts, separatorParts, semanticModel, position)); var state = GetCurrentArgumentState(root, position, syntaxFacts, targetExpression.FullSpan, cancellationToken); return CreateSignatureHelpItems(items, targetExpression.Span, state, selectedItemIndex: null, parameterIndexOverride: -1); @@ -192,7 +191,7 @@ private static IEnumerable ConvertTupleMembers(INamedTyp typeParts.Add(new SymbolDisplayPart(SymbolDisplayPartKind.PropertyName, null, elementName)); } - result.Add(new SignatureHelpParameter(name: string.Empty, isOptional: false, documentationFactory: null, displayParts: typeParts)); + result.Add(new SignatureHelpParameter(name: string.Empty, isOptional: false, documentationFactory: null, displayParts: [.. typeParts])); } return result; diff --git a/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs b/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs index 6d0e667a3be..72cb2ec588c 100644 --- a/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs @@ -55,7 +55,7 @@ internal sealed class UseExpressionBodyCodeRefactoringProvider() : SyntaxEditorB } } - protected override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -184,7 +184,7 @@ private static SyntaxNode GetUpdatedRoot( return root.ReplaceNode(parent, updatedParent); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs b/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs index 6ab1e4fde3d..d1172e63e39 100644 --- a/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs +++ b/src/roslyn/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs @@ -19,51 +19,51 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ConvertAutoPropertyToFu public partial class ConvertAutoPropertyToFullPropertyTests { private OptionsCollection PreferExpressionBodiedAccessorsWhenPossible - => new OptionsCollection(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement } }; + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement } }; private OptionsCollection PreferExpressionBodiedAccessorsWhenOnSingleLine - => new OptionsCollection(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenOnSingleLineWithSilentEnforcement } }; + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenOnSingleLineWithSilentEnforcement } }; private OptionsCollection DoNotPreferExpressionBodiedAccessors - => new OptionsCollection(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement } }; + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement } }; private OptionsCollection DoNotPreferExpressionBodiedAccessorsAndPropertyOpenBraceOnSameLine - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpFormattingOptions2.NewLineBeforeOpenBrace, NewLineBeforeOpenBracePlacement.All & ~NewLineBeforeOpenBracePlacement.Properties }, }; private OptionsCollection DoNotPreferExpressionBodiedAccessorsAndAccessorOpenBraceOnSameLine - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpFormattingOptions2.NewLineBeforeOpenBrace, NewLineBeforeOpenBracePlacement.All & ~NewLineBeforeOpenBracePlacement.Accessors }, }; private OptionsCollection PreferExpressionBodiesOnAccessorsAndMethods - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseCustomFieldName - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { NamingStyleOptions.NamingPreferences, CreateCustomFieldNamingStylePreference() }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseUnderscorePrefixedFieldName - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { NamingStyleOptions.NamingPreferences, CreateUnderscorePrefixedFieldNamingStylePreference() }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseCustomStaticFieldName - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { NamingStyleOptions.NamingPreferences, CreateCustomStaticFieldNamingStylePreference() }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, diff --git a/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs b/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs index 6d4a3be03c8..8f905c6bae2 100644 --- a/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs +++ b/src/roslyn/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs @@ -24,7 +24,7 @@ public sealed partial class ConvertForEachToForTests : AbstractCSharpCodeActionT protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertForEachToForCodeRefactoringProvider(); - private readonly CodeStyleOption2 onWithSilent = new CodeStyleOption2(true, NotificationOption2.Silent); + private readonly CodeStyleOption2 onWithSilent = new(true, NotificationOption2.Silent); private OptionsCollection ImplicitTypeEverywhere => new(GetLanguage()) diff --git a/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs b/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs index 8168a715116..1b228a291ff 100644 --- a/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs +++ b/src/roslyn/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs @@ -22,10 +22,10 @@ public sealed class ConvertForToForEachTests : AbstractCSharpCodeActionTest_NoEd protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) => new CSharpConvertForToForEachCodeRefactoringProvider(); - private readonly CodeStyleOption2 onWithSilent = new CodeStyleOption2(true, NotificationOption2.Silent); + private readonly CodeStyleOption2 onWithSilent = new(true, NotificationOption2.Silent); private OptionsCollection ImplicitTypeEverywhere() - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.VarElsewhere, onWithSilent }, { CSharpCodeStyleOptions.VarWhenTypeIsApparent, onWithSilent }, diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs index 60033709f3b..efd13f961d7 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/AllAnalyzersSeverityConfigurationTests.cs @@ -22,7 +22,7 @@ public abstract partial class AllAnalyzersSeverityConfigurationTests : AbstractS { private sealed class CustomDiagnosticAnalyzer : DiagnosticAnalyzer { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + private static readonly DiagnosticDescriptor Rule = new( id: "XYZ0001", title: "Title", messageFormat: "Message", diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs index b4e367a7025..4b15d6504fc 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/CategoryBasedSeverityConfigurationTests.cs @@ -22,7 +22,7 @@ public abstract partial class CategoryBasedSeverityConfigurationTests : Abstract { private sealed class CustomDiagnosticAnalyzer : DiagnosticAnalyzer { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + private static readonly DiagnosticDescriptor Rule = new( id: "XYZ0001", title: "Title", messageFormat: "Message", diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs index 576b46278a0..f1d5950ecf1 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Configuration/ConfigureSeverity/DotNetDiagnosticSeverityBasedSeverityConfigurationTests.cs @@ -22,7 +22,7 @@ public abstract partial class DotNetDiagnosticSeverityBasedSeverityConfiguration { private sealed class CustomDiagnosticAnalyzer : DiagnosticAnalyzer { - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + private static readonly DiagnosticDescriptor Rule = new( id: "XYZ0001", title: "Title", messageFormat: "Message", diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs index c875ec80500..5463bda2728 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveSuppressionTests.cs @@ -27,7 +27,7 @@ protected sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly bool _reportDiagnosticsWithoutLocation; public static readonly DiagnosticDescriptor Decsciptor = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs index 4077ff2f4b1..9d99b7cab68 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/RemoveUnnecessaryPragmaSuppressionsTests.cs @@ -49,9 +49,9 @@ protected override TestParameters SetParameterDefaults(TestParameters parameters protected sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Descriptor0168 = - new DiagnosticDescriptor("Analyzer0168", "Variable is declared but never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("Analyzer0168", "Variable is declared but never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor Descriptor0219 = - new DiagnosticDescriptor("Analyzer0219", "Variable is assigned but its value is never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("Analyzer0219", "Variable is assigned but its value is never used", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor0168, Descriptor0219]; @@ -106,7 +106,7 @@ public override void Initialize(AnalysisContext context) protected sealed class CompilationEndDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("CompilationEndId", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true, + new("CompilationEndId", "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true, customTags: [WellKnownDiagnosticTags.CompilationEnd]); public override ImmutableArray SupportedDiagnostics => [Descriptor]; public override void Initialize(AnalysisContext context) diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs index e0e17228856..3bf87eeb578 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs @@ -28,7 +28,7 @@ protected override TestWorkspace CreateWorkspaceFromFile(string definition, Pars => TestWorkspace.CreateCSharp(definition, (CSharpParseOptions)parseOptions, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService); internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) - => new Tuple(new Analyzer(), new CSharpSuppressionCodeFixProvider()); + => new(new Analyzer(), new CSharpSuppressionCodeFixProvider()); [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1007071")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/956453")] diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs index 86fb78b7173..b2d432cc4d6 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTest_FixMultipleTests.cs @@ -36,9 +36,9 @@ internal override Tuple CreateDia private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor1 = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public static readonly DiagnosticDescriptor Decsciptor2 = - new DiagnosticDescriptor("InfoDiagnostic2", "InfoDiagnostic2 Title", "InfoDiagnostic2", "InfoDiagnostic2", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic2", "InfoDiagnostic2 Title", "InfoDiagnostic2", "InfoDiagnostic2", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Decsciptor1, Decsciptor2]; diff --git a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs index 090d130d7cb..e85e30f06f9 100644 --- a/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs @@ -492,7 +492,7 @@ void Method() var allFixes = (await fixService.GetFixesAsync(document, span, CancellationToken.None)) .SelectMany(fixCollection => fixCollection.Fixes); - var cs0219Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0219").ToArray(); + var cs0219Fixes = allFixes.Where(fix => fix.Diagnostics.First().Id == "CS0219").ToArray(); // Ensure that there are no duplicate suppression fixes. Assert.Equal(1, cs0219Fixes.Length); @@ -502,7 +502,7 @@ void Method() // Ensure that there *is* a fix for the other warning and that it has a *different* // equivalence key so that it *doesn't* get de-duplicated Assert.Equal(1, diagnostics.Where(d => d.Id == "CS0168").Count()); - var cs0168Fixes = allFixes.Where(fix => fix.PrimaryDiagnostic.Id == "CS0168"); + var cs0168Fixes = allFixes.Where(fix => fix.Diagnostics.First().Id == "CS0168"); var cs0168EquivalenceKey = cs0168Fixes.Single().Action.EquivalenceKey; Assert.NotNull(cs0168EquivalenceKey); Assert.NotEqual(cs0219EquivalenceKey, cs0168EquivalenceKey); @@ -731,7 +731,7 @@ public sealed partial class UserInfoDiagnosticSuppressionTests : CSharpPragmaWar private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -845,7 +845,7 @@ public sealed class UserErrorDiagnosticSuppressionTests : CSharpPragmaWarningDis private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor("ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", DiagnosticSeverity.Error, isEnabledByDefault: true); + new("ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", "ErrorDiagnostic", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -910,7 +910,7 @@ public sealed class DiagnosticWithBadIdSuppressionTests : CSharpPragmaWarningDis private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor("@~DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("@~DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", "DiagnosticWithBadId", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { @@ -959,7 +959,7 @@ public sealed partial class MultilineDiagnosticSuppressionTests : CSharpPragmaWa private sealed class UserDiagnosticAnalyzer : DiagnosticAnalyzer { public static readonly DiagnosticDescriptor Decsciptor = - new DiagnosticDescriptor("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); + new("InfoDiagnostic", "InfoDiagnostic Title", "InfoDiagnostic", "InfoDiagnostic", DiagnosticSeverity.Info, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics { diff --git a/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs b/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs index 68b9e7d3076..d10d41086e7 100644 --- a/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs +++ b/src/roslyn/src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs @@ -37,7 +37,7 @@ public void KnownMatches() var knownMatches = new KeyValuePair[] { - new KeyValuePair(((BlockSyntax)m1.RootNodes.First()).Statements[1], ((BlockSyntax)m2.RootNodes.First()).Statements[0]) + new(((BlockSyntax)m1.RootNodes.First()).Statements[1], ((BlockSyntax)m2.RootNodes.First()).Statements[0]) }; // pre-matched: diff --git a/src/roslyn/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/roslyn/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 8fa4682670c..e063b2f2825 100644 --- a/src/roslyn/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/roslyn/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -2707,8 +2707,12 @@ public void Type_DeleteInsert_Reloadable() { var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }"; var srcA2 = ReloadableAttributeSrc; + + var srcB1 = ""; + var srcB2 = "using System.Runtime.CompilerServices; [CreateNewOnMetadataUpdate]class C { void F() {} }"; + EditAndContinueValidation.VerifySemantics( - [GetTopEdits(srcA1, srcA2), GetTopEdits("", "[CreateNewOnMetadataUpdate]class C { void F() {} }")], + [GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)], [ DocumentResults(), DocumentResults( @@ -5408,7 +5412,7 @@ public void NestedType_Replace_WithUpdateInNestedType_Partial_DifferentDocument( ]), DocumentResults(semanticEdits: [ - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M")) + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") ]) ], capabilities: EditAndContinueCapabilities.NewTypeDefinition); @@ -5427,7 +5431,6 @@ public void NestedType_Replace_WithUpdateInNestedType_Partial_SameDocument() semanticEdits: [ SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")), - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M")) ], capabilities: EditAndContinueCapabilities.NewTypeDefinition); } @@ -5444,8 +5447,7 @@ public void NestedType_Replace_WithUpdateInNestedType() edits, semanticEdits: [ - SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")), - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M")) + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")) ], capabilities: EditAndContinueCapabilities.NewTypeDefinition); } @@ -5875,6 +5877,36 @@ public void NestedType_ClassDeleteInsert() Diagnostic(RudeEditKind.Move, "public class X", FeaturesResources.class_)); } + /// + /// Scenario: Razor page types are marked with CreateNewOnMetadataUpdateAttribute. + /// It is possible to define nested types via @functions block and any changes to this block should be allowed. + /// + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76989")] + public void NestedType_Enum_Update() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { enum E { A } }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { enum E { A, B } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + semanticEdits: [SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))], + capabilities: EditAndContinueCapabilities.NewTypeDefinition); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76989")] + public void NestedType_Rename_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { class D1 { public void F() { Console.WriteLine(1); }}; }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { class D2 { public void F() { Console.WriteLine(2); }}; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + semanticEdits: [SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))], + capabilities: EditAndContinueCapabilities.NewTypeDefinition); + } + /// /// A new generic type can be added whether it's nested and inherits generic parameters from the containing type, or top-level. /// @@ -8496,6 +8528,21 @@ public void Namespace_Insert_NewType_Qualified(string src2) capabilities: EditAndContinueCapabilities.NewTypeDefinition); } + [Fact] + public void Namespace_Insert_NewType_HidingMetadataType() + { + var srcA1 = ""; + var srcA2 = """ + namespace Microsoft.CodeAnalysis; + public readonly partial class EmbeddedAttribute; + """; + + EditAndContinueValidation.VerifySemantics( + GetTopEdits(srcA1, srcA2), + [SemanticEdit(SemanticEditKind.Insert, c => c.Assembly.GlobalNamespace.GetMember("Microsoft").GetMember("CodeAnalysis").GetMember("EmbeddedAttribute"))], + capabilities: EditAndContinueCapabilities.NewTypeDefinition); + } + [Theory] [InlineData("class")] [InlineData("interface")] @@ -8759,7 +8806,12 @@ public void Namespace_Update_FileScoped() [Fact] public void Namespace_Update_MultiplePartials1() => EditAndContinueValidation.VerifySemantics( - [GetTopEdits(@"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }", @"namespace N { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"), GetTopEdits(@"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }", @"namespace M { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }")], + [GetTopEdits( + @"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }", + @"namespace N { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"), + GetTopEdits( + @"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }", + @"namespace M { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }")], [ DocumentResults( semanticEdits: @@ -8777,7 +8829,12 @@ public void Namespace_Update_MultiplePartials1() [Fact] public void Namespace_Update_MultiplePartials2() => EditAndContinueValidation.VerifySemantics( - [GetTopEdits(@"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }", @"namespace M { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"), GetTopEdits(@"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }", @"namespace M { partial class/*3*/C {} } namespace M { partial class/*4*/C {} }")], + [GetTopEdits( + @"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }", + @"namespace M { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"), + GetTopEdits( + @"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }", + @"namespace M { partial class/*3*/C {} } namespace M { partial class/*4*/C {} }")], [ DocumentResults(diagnostics: [ diff --git a/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index 2f00d6ba5a1..930f6ade210 100644 --- a/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/roslyn/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -28,7 +28,7 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWor protected override ImmutableArray MassageActions(ImmutableArray actions) => GetNestedActions(actions); - private readonly CodeStyleOption2 onWithInfo = new CodeStyleOption2(true, NotificationOption2.Suggestion); + private readonly CodeStyleOption2 onWithInfo = new(true, NotificationOption2.Suggestion); // specify all options explicitly to override defaults. private OptionsCollection ImplicitTypingEverywhere() @@ -8455,4 +8455,46 @@ public Task TestNotOnNamedType2() [||]Console.WriteLine(); """); + + [Fact] + public Task TestInTopLevelLoop1() + => TestAsync( + """ + for (var i = 0; i < 10; i++) + { + Console.Write([|args[i]|]); + } + """, + """ + for (var i = 0; i < 10; i++) + { + string {|Rename:v|} = args[i]; + Console.Write(v); + } + """, + new(parseOptions: TestOptions.Regular)); + + [Fact] + public Task TestInTopLevelLoop2() + => TestAsync( + """ + void M(object[] p) + { + for (var i = 0; i < 10; i++) + { + Console.Write([|p[i]|]); + } + } + """, + """ + void M(object[] p) + { + for (var i = 0; i < 10; i++) + { + object {|Rename:v|} = p[i]; + Console.Write(v); + } + } + """, + new(parseOptions: TestOptions.Regular)); } diff --git a/src/roslyn/src/Features/CSharpTest/InvertIf/InvertIfTests.cs b/src/roslyn/src/Features/CSharpTest/InvertIf/InvertIfTests.cs index 72d51817405..0c42efe4cac 100644 --- a/src/roslyn/src/Features/CSharpTest/InvertIf/InvertIfTests.cs +++ b/src/roslyn/src/Features/CSharpTest/InvertIf/InvertIfTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.InvertIf; @@ -17,8 +18,8 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf; public sealed partial class InvertIfTests { private static Task TestInsideMethodAsync( - string initial, - string expected) + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initial, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected) { return TestAsync(CreateTreeText(initial), CreateTreeText(expected)); @@ -41,7 +42,10 @@ void Goo() } } - private static Task TestAsync(string initial, string expected, LanguageVersion languageVersion = LanguageVersion.Latest) + private static Task TestAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initial, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, + LanguageVersion languageVersion = LanguageVersion.Latest) => new CSharpCodeRefactoringVerifier.Test { TestCode = initial, @@ -1474,4 +1478,164 @@ class C { if (!b) { return (false); } return (true); } } """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective1() + => TestAsync(""" + [||]#if true + #else + #endif + """, """ + #if false + #else + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective2() + => TestAsync(""" + [||]#if true + #else + + #endif + """, """ + #if false + + #else + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective3() + => TestAsync(""" + [||]#if true + + #else + #endif + """, """ + #if false + #else + + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective4() + => TestAsync(""" + [||]#if true + class C + { + } + #else + record D(); + #endif + """, """ + #if false + record D(); + #else + class C + { + } + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective5() + => TestAsync(""" + [||]#if !true + class C + { + } + #else + record D(); + #endif + """, """ + #if true + record D(); + #else + class C + { + } + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective6() + => TestAsync(""" + [||]#if NAME + class C + { + } + #else + record D(); + #endif + """, """ + #if !NAME + record D(); + #else + class C + { + } + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective7() + => TestAsync(""" + [||]#if A && B + class C + { + } + #else + record D(); + #endif + """, """ + #if !(A && B) + record D(); + #else + class C + { + } + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective8() + => TestAsync(""" + [||]#if (true) + class C + { + } + #else + record D(); + #endif + """, """ + #if (false) + record D(); + #else + class C + { + } + #endif + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75438")] + public Task TestIfDirective9() + => TestAsync(""" + [||]#if (true) + class C + { + } + #else + record D(); + #endif + """, """ + #if (false) + record D(); + #else + class C + { + } + #endif + """); } diff --git a/src/roslyn/src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs b/src/roslyn/src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs index e4b3dbdffe2..4e54127eda4 100644 --- a/src/roslyn/src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs +++ b/src/roslyn/src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs @@ -6884,6 +6884,80 @@ public static bool TryFirst(this IEnumerable source, Func predica } """); + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72420")] + public Task TestGenericWithNullableStruct() + => TestMissingInRegularAndScriptAsync( + """ + using System; + + class C + { + public void T() + { + static void test(Func func) + { + } + + // IDE0001 NOT expected for this call + [|test|](() => new UnmanagedCustomStruct()); + } + + public struct UnmanagedCustomStruct + { + public Guid Foo; + public int Bar; + } + } + """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72420")] + public Task TestGenericWithStruct() + => TestInRegularAndScriptAsync( + """ + using System; + + class C + { + public void T() + { + static void test(Func func) + { + } + + // IDE0001 expected for this call + [|test|](() => new UnmanagedCustomStruct()); + } + + public struct UnmanagedCustomStruct + { + public Guid Foo; + public int Bar; + } + } + """, + """ + using System; + + class C + { + public void T() + { + static void test(Func func) + { + } + + // IDE0001 expected for this call + test(() => new UnmanagedCustomStruct()); + } + + public struct UnmanagedCustomStruct + { + public Guid Foo; + public int Bar; + } + } + """); + private async Task TestWithPredefinedTypeOptionsAsync(string code, string expected, int index = 0) => await TestInRegularAndScriptAsync(code, expected, index, new TestParameters(options: PreferIntrinsicTypeEverywhere)); diff --git a/src/roslyn/src/Features/CSharpTest/Snippets/CSharpReversedForSnippetProviderTests.cs b/src/roslyn/src/Features/CSharpTest/Snippets/CSharpReversedForSnippetProviderTests.cs index ed56c20ff02..e40af7fe95b 100644 --- a/src/roslyn/src/Features/CSharpTest/Snippets/CSharpReversedForSnippetProviderTests.cs +++ b/src/roslyn/src/Features/CSharpTest/Snippets/CSharpReversedForSnippetProviderTests.cs @@ -142,7 +142,7 @@ public void Method() { void LocalFunction() { - for (global::System.Int32 {|0:i|} = {|1:(length)|} - (1); {|0:i|} >= 0; {|0:i|}--) + for (global::System.Int32 {|0:i|} = {|1:length|} - (1); {|0:i|} >= 0; {|0:i|}--) { $$ } @@ -171,7 +171,7 @@ public void Method() { var action = delegate() { - for (global::System.Int32 {|0:i|} = {|1:(length)|} - (1); {|0:i|} >= 0; {|0:i|}--) + for (global::System.Int32 {|0:i|} = {|1:length|} - (1); {|0:i|} >= 0; {|0:i|}--) { $$ } @@ -200,7 +200,7 @@ public void Method() { var action = () => { - for (global::System.Int32 {|0:i|} = {|1:(length)|} - (1); {|0:i|} >= 0; {|0:i|}--) + for (global::System.Int32 {|0:i|} = {|1:length|} - (1); {|0:i|} >= 0; {|0:i|}--) { $$ } diff --git a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs index de64831213a..6efb041ed53 100644 --- a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs +++ b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs @@ -23,7 +23,7 @@ private OptionsCollection UseBlockBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithSilentEnforcement); private OptionsCollection UseBlockBodyForMethodsAndAccessorsAndProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, diff --git a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs index e8ca5722864..51ec5b32a06 100644 --- a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs +++ b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs @@ -24,56 +24,56 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWor => new UseExpressionBodyCodeRefactoringProvider(); private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, }; private OptionsCollection UseBlockBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.Never, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.Never, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, diff --git a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs index b42a08165ac..40e4a00748d 100644 --- a/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs +++ b/src/roslyn/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs @@ -23,49 +23,49 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWor => new UseExpressionBodyCodeRefactoringProvider(); private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.WhenPossible, NotificationOption2.None }, }; private OptionsCollection UseBlockBodyForAccessors_ExpressionBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSilentEnforcement }, }; private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic - => new OptionsCollection(GetLanguage()) + => new(GetLanguage()) { { CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ExpressionBodyPreference.Never, NotificationOption2.None }, { CSharpCodeStyleOptions.PreferExpressionBodiedProperties, ExpressionBodyPreference.Never, NotificationOption2.None }, diff --git a/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs index 5b811b6feb8..d2eaf81ad4d 100644 --- a/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs @@ -27,8 +27,8 @@ internal abstract class AbstractAddFileBannerCodeRefactoringProvider : SyntaxEdi protected abstract bool IsCommentStartCharacter(char ch); - protected sealed override ImmutableArray SupportedFixAllScopes { get; } - = [FixAllScope.Project, FixAllScope.Solution]; + protected sealed override ImmutableArray SupportedRefactorAllScopes { get; } + = [RefactorAllScope.Project, RefactorAllScope.Solution]; public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -145,7 +145,7 @@ private async Task> TryGetBannerAsync( return bannerService.GetFileBanner(token); } - protected sealed override async Task FixAllAsync( + protected sealed override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 51df1ba2f1e..a71d1acb7b4 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -39,7 +40,7 @@ internal abstract partial class AbstractAddImportFeatureService GetImportNamespacesInScope(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); protected abstract ITypeSymbol GetDeconstructInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); protected abstract ITypeSymbol GetQueryClauseInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); - protected abstract bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken); protected abstract Task AddImportAsync(SyntaxNode contextNode, INamespaceOrTypeSymbol symbol, Document document, AddImportPlacementOptions options, CancellationToken cancellationToken); protected abstract Task AddImportAsync(SyntaxNode contextNode, IReadOnlyList nameSpaceParts, Document document, AddImportPlacementOptions options, CancellationToken cancellationToken); - protected abstract bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel); + protected abstract bool IsAddMethodContext( + SyntaxNode node, SemanticModel semanticModel, [NotNullWhen(true)] out SyntaxNode? objectCreationExpression); protected abstract string GetDescription(IReadOnlyList nameParts); protected abstract (string description, bool hasExistingImport) GetDescription(Document document, AddImportPlacementOptions options, INamespaceOrTypeSymbol symbol, SemanticModel semanticModel, SyntaxNode root, CancellationToken cancellationToken); @@ -143,7 +144,7 @@ private async Task> FindResultsAsync( // Caches so we don't produce the same data multiple times while searching // all over the solution. var project = document.Project; - var projectToAssembly = new ConcurrentDictionary>(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); + var projectToAssembly = new ConcurrentDictionary>(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); var referenceToCompilation = new ConcurrentDictionary(concurrencyLevel: 2, capacity: project.Solution.Projects.Sum(p => p.MetadataReferences.Count)); var finder = new SymbolReferenceFinder( @@ -168,7 +169,7 @@ private static bool IsHostOrRemoteWorkspace(Project project) => project.Solution.WorkspaceKind is WorkspaceKind.Host or WorkspaceKind.RemoteWorkspace; private async Task> FindResultsAsync( - ConcurrentDictionary> projectToAssembly, + ConcurrentDictionary> projectToAssembly, ConcurrentDictionary referenceToCompilation, Project project, int maxResults, @@ -213,7 +214,7 @@ private static async Task FindResultsInAllSymbolsInStartingProjectAsync( } private static async Task FindResultsInUnreferencedProjectSourceSymbolsAsync( - ConcurrentDictionary> projectToAssembly, + ConcurrentDictionary> projectToAssembly, Project project, ConcurrentQueue allSymbolReferences, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { @@ -478,31 +479,6 @@ private static void AddRange(ConcurrentQueue allSymbolReferences, Imm allSymbolReferences.Enqueue(reference); } - protected static bool IsViableExtensionMethod(IMethodSymbol method, ITypeSymbol receiver) - { - if (receiver == null || method == null) - { - return false; - } - - // It's possible that the 'method' we're looking at is from a different language than - // the language we're currently in. For example, we might find the extension method - // in an unreferenced VB project while we're in C#. However, in order to 'reduce' - // the extension method, the compiler requires both the method and receiver to be - // from the same language. - // - // So, if they're not from the same language, we simply can't proceed. Now in this - // case we decide that the method is not viable. But we could, in the future, decide - // to just always consider such methods viable. - - if (receiver.Language != method.Language) - { - return false; - } - - return method.ReduceExtensionMethod(receiver) != null; - } - private static bool NotGlobalNamespace(SymbolReference reference) { var symbol = reference.SymbolResult.Symbol; diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 01fc2d7372b..70efacea83e 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.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. -#nullable disable - using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; @@ -67,7 +66,7 @@ public SymbolReferenceFinder( _symbolSearchService = symbolSearchService; Options = options; _packageSources = packageSources; - _syntaxFacts = document.GetLanguageService(); + _syntaxFacts = document.GetRequiredLanguageService(); _namespacesInScope = GetNamespacesInScope(cancellationToken); _isWithinImport = owner.IsWithinImport(node); @@ -96,7 +95,7 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co internal Task> FindInAllSymbolsInStartingProjectAsync(bool exact, CancellationToken cancellationToken) => DoAsync(new AllSymbolsProjectSearchScope(_owner, _document.Project, exact), cancellationToken); - internal Task> FindInSourceSymbolsInProjectAsync(ConcurrentDictionary> projectToAssembly, Project project, bool exact, CancellationToken cancellationToken) + internal Task> FindInSourceSymbolsInProjectAsync(ConcurrentDictionary> projectToAssembly, Project project, bool exact, CancellationToken cancellationToken) => DoAsync(new SourceSymbolsProjectSearchScope(_owner, projectToAssembly, project, exact), cancellationToken); internal Task> FindInMetadataSymbolsAsync(IAssemblySymbol assembly, Project assemblyProject, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) @@ -117,7 +116,7 @@ private async Task> DoAsync(SearchScope searchSc tasks.Add(GetReferencesForMatchingTypesAsync(searchScope, cancellationToken)); tasks.Add(GetReferencesForMatchingNamespacesAsync(searchScope, cancellationToken)); tasks.Add(GetReferencesForMatchingFieldsAndPropertiesAsync(searchScope, cancellationToken)); - tasks.Add(GetReferencesForMatchingExtensionMethodsAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForMatchingExtensionMembersAsync(searchScope, cancellationToken)); // Searching for things like "Add" (for collection initializers) and "Select" // (for extension methods) should only be done when doing an 'exact' search. @@ -165,7 +164,7 @@ private static void CalculateContext( syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out name, out arity); inAttributeContext = syntaxFacts.IsNameOfAttribute(nameNode); - hasIncompleteParentMember = nameNode?.Parent?.RawKind == syntaxFacts.SyntaxKinds.IncompleteMember; + hasIncompleteParentMember = nameNode.GetRequiredParent().RawKind == syntaxFacts.SyntaxKinds.IncompleteMember; looksGeneric = syntaxFacts.LooksGeneric(nameNode); } @@ -186,7 +185,7 @@ private async Task> GetReferencesForMatchingType out var name, out var arity, out var inAttributeContext, out var hasIncompleteParentMember, out var looksGeneric); - if (ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken: cancellationToken)) + if (ExpressionBinds(nameNode, checkForExtensionMembers: false, cancellationToken: cancellationToken)) { // If the expression bound, there's nothing to do. return []; @@ -268,7 +267,7 @@ private async Task> GetReferencesForMatchingName _syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out var arity); if (arity == 0 && - !ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken)) + !ExpressionBinds(nameNode, checkForExtensionMembers: false, cancellationToken)) { var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Namespace, cancellationToken).ConfigureAwait(false); var namespaceSymbols = OfType(symbols); @@ -290,18 +289,18 @@ private async Task> GetReferencesForMatchingFiel SearchScope searchScope, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode) && + if (_owner.CanAddImportForMember(_diagnosticId, _syntaxFacts, _node, out var nameNode) && nameNode != null) { // We have code like "Color.Black". "Color" bound to a 'Color Color' property, and // 'Black' did not bind. We want to find a type called 'Color' that will actually // allow 'Black' to bind. - var syntaxFacts = _document.GetLanguageService(); + var syntaxFacts = _document.GetRequiredLanguageService(); if (syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) || syntaxFacts.IsNameOfMemberBindingExpression(nameNode)) { var expression = syntaxFacts.IsNameOfSimpleMemberAccessExpression(nameNode) - ? syntaxFacts.GetExpressionOfMemberAccessExpression(nameNode.Parent, allowImplicitTarget: true) + ? syntaxFacts.GetExpressionOfMemberAccessExpression(nameNode.GetRequiredParent(), allowImplicitTarget: true) : syntaxFacts.GetTargetOfMemberBinding(nameNode.Parent); if (expression is TSimpleNameSyntax simpleName) { @@ -346,61 +345,72 @@ m is IFieldSymbol or IPropertySymbol && } /// - /// Searches for extension methods that match the name the user has written. Returns - /// s to the s that contain - /// the static classes that those extension methods are contained in. + /// Searches for extension members that match the name the user has written. Returns s to the s that contain the static classes that those + /// extension methods are contained in. /// - private async Task> GetReferencesForMatchingExtensionMethodsAsync( + private async Task> GetReferencesForMatchingExtensionMembersAsync( SearchScope searchScope, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode) && + if (_owner.CanAddImportForMember(_diagnosticId, _syntaxFacts, _node, out var nameNode) && nameNode != null) { cancellationToken.ThrowIfCancellationRequested(); // See if the name binds. If it does, there's nothing further we need to do. - if (!ExpressionBinds(nameNode, checkForExtensionMethods: true, cancellationToken)) + if (!ExpressionBinds(nameNode, checkForExtensionMembers: true, cancellationToken)) { _syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out var arity); if (name != null) { - var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Member, cancellationToken).ConfigureAwait(false); + var (receiverType, isStatic) = GetReceiverType(); + if (receiverType is null) + return []; + + var symbols = await searchScope.FindDeclarationsAsync( + name, nameNode, SymbolFilter.Member, cancellationToken).ConfigureAwait(false); - var methodSymbols = OfType(symbols); + var classicExtensionMethods = OfType(symbols) + .WhereAsArray(s => IsViableClassicExtensionMethod(s.Symbol, receiverType, predicate: null)); - var extensionMethodSymbols = GetViableExtensionMethods( - methodSymbols, nameNode.Parent, cancellationToken); + var modernExtensionMembers = symbols + .WhereAsArray(s => IsViableModernExtensionMember(s.Symbol, receiverType) && s.Symbol.IsStatic == isStatic); - var namespaceSymbols = extensionMethodSymbols.SelectAsArray(s => s.WithSymbol(s.Symbol.ContainingNamespace)); - return GetNamespaceSymbolReferences(searchScope, namespaceSymbols); + var classicExtensionNamespaces = classicExtensionMethods.Select(s => s.WithSymbol(s.Symbol.ContainingNamespace)); + var modernExtensionNamespaces = modernExtensionMembers.Select(s => s.WithSymbol(s.Symbol.ContainingNamespace)); + var allExtensionNamespaces = classicExtensionNamespaces.Concat(modernExtensionNamespaces).ToImmutableArray(); + + return GetNamespaceSymbolReferences(searchScope, allExtensionNamespaces); } } } return []; - } - - private ImmutableArray> GetViableExtensionMethods( - ImmutableArray> methodSymbols, - SyntaxNode expression, CancellationToken cancellationToken) - { - return GetViableExtensionMethodsWorker(methodSymbols).WhereAsArray( - s => _owner.IsViableExtensionMethod(s.Symbol, expression, _semanticModel, _syntaxFacts, cancellationToken)); - } - private ImmutableArray> GetViableExtensionMethods( - ImmutableArray> methodSymbols, ITypeSymbol typeSymbol) - { - return GetViableExtensionMethodsWorker(methodSymbols).WhereAsArray( - s => IsViableExtensionMethod(s.Symbol, typeSymbol)); + (ITypeSymbol? receiverType, bool isStatic) GetReceiverType() + { + var expression = nameNode.GetRequiredParent(); + if (_syntaxFacts.IsInvocationExpression(expression)) + return (_semanticModel.GetEnclosingNamedType(expression.SpanStart, cancellationToken), isStatic: false); + + var leftExpression = + _syntaxFacts.IsMemberAccessExpression(expression) ? _syntaxFacts.GetExpressionOfMemberAccessExpression(expression, allowImplicitTarget: true) : + _syntaxFacts.IsMemberBindingExpression(expression) ? _syntaxFacts.GetTargetOfMemberBinding(expression) : null; + if (leftExpression == null) + return default; + + var symbol = _semanticModel.GetSymbolInfo(leftExpression, cancellationToken).GetAnySymbol(); + var semanticInfo = _semanticModel.GetTypeInfo(leftExpression, cancellationToken); + return (semanticInfo.Type, isStatic: symbol is ITypeSymbol); + } } private ImmutableArray> GetViableExtensionMethodsWorker( ImmutableArray> methodSymbols) { return methodSymbols.WhereAsArray( - s => s.Symbol.IsExtensionMethod && + s => s.Symbol.IsClassicOrModernInstanceExtensionMethod() && s.Symbol.IsAccessibleWithin(_semanticModel.Compilation.Assembly)); } @@ -413,23 +423,13 @@ private async Task> GetReferencesForCollectionIn SearchScope searchScope, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out _) && + if (_owner.CanAddImportForMember(_diagnosticId, _syntaxFacts, _node, out _) && !_syntaxFacts.IsSimpleName(_node) && - _owner.IsAddMethodContext(_node, _semanticModel)) + _owner.IsAddMethodContext(_node, _semanticModel, out var objectCreationExpression)) { - var symbols = await searchScope.FindDeclarationsAsync( - nameof(IList.Add), nameNode: null, filter: SymbolFilter.Member, cancellationToken).ConfigureAwait(false); - - // Note: there is no desiredName for these search results. We're searching for - // extension methods called "Add", but we have no intention of renaming any - // of the existing user code to that name. - var methodSymbols = OfType(symbols).SelectAsArray(s => s.WithDesiredName(null)); - - var viableMethods = GetViableExtensionMethods( - methodSymbols, _node.Parent, cancellationToken); - - return GetNamespaceSymbolReferences(searchScope, - viableMethods.SelectAsArray(m => m.WithSymbol(m.Symbol.ContainingNamespace))); + var objectCreationType = _semanticModel.GetTypeInfo(objectCreationExpression, cancellationToken).Type; + return await GetReferencesForExtensionMethodAsync( + searchScope, nameof(IList.Add), objectCreationType, predicate: null, cancellationToken).ConfigureAwait(false); } return []; @@ -472,13 +472,10 @@ private async Task> GetReferencesForGetAwaiterAs if (_owner.CanAddImportForGetAwaiter(_diagnosticId, _syntaxFacts, _node)) { var type = GetAwaitInfo(_semanticModel, _syntaxFacts, _node); - if (type != null) - { - return await GetReferencesForExtensionMethodAsync( - searchScope, WellKnownMemberNames.GetAwaiter, type, - static m => m.IsValidGetAwaiter(), - cancellationToken).ConfigureAwait(false); - } + return await GetReferencesForExtensionMethodAsync( + searchScope, WellKnownMemberNames.GetAwaiter, type, + static m => m.IsValidGetAwaiter(), + cancellationToken).ConfigureAwait(false); } return []; @@ -497,13 +494,10 @@ private async Task> GetReferencesForGetEnumerato if (_owner.CanAddImportForGetEnumerator(_diagnosticId, _syntaxFacts, _node)) { var type = GetCollectionExpressionType(_semanticModel, _syntaxFacts, _node); - if (type != null) - { - return await GetReferencesForExtensionMethodAsync( - searchScope, WellKnownMemberNames.GetEnumeratorMethodName, type, - static m => m.IsValidGetEnumerator(), - cancellationToken).ConfigureAwait(false); - } + return await GetReferencesForExtensionMethodAsync( + searchScope, WellKnownMemberNames.GetEnumeratorMethodName, type, + static m => m.IsValidGetEnumerator(), + cancellationToken).ConfigureAwait(false); } return []; @@ -522,13 +516,10 @@ private async Task> GetReferencesForGetAsyncEnum if (_owner.CanAddImportForGetAsyncEnumerator(_diagnosticId, _syntaxFacts, _node)) { var type = GetCollectionExpressionType(_semanticModel, _syntaxFacts, _node); - if (type != null) - { - return await GetReferencesForExtensionMethodAsync( - searchScope, WellKnownMemberNames.GetAsyncEnumeratorMethodName, type, - static m => m.IsValidGetAsyncEnumerator(), - cancellationToken).ConfigureAwait(false); - } + return await GetReferencesForExtensionMethodAsync( + searchScope, WellKnownMemberNames.GetAsyncEnumeratorMethodName, type, + static m => m.IsValidGetAsyncEnumerator(), + cancellationToken).ConfigureAwait(false); } return []; @@ -562,36 +553,94 @@ private async Task> GetReferencesForDeconstructA } private async Task> GetReferencesForExtensionMethodAsync( - SearchScope searchScope, string name, ITypeSymbol type, Func predicate, CancellationToken cancellationToken) + SearchScope searchScope, string name, ITypeSymbol? type, Func? predicate, CancellationToken cancellationToken) { + if (type is null) + return []; + var symbols = await searchScope.FindDeclarationsAsync( name, nameNode: null, filter: SymbolFilter.Member, cancellationToken).ConfigureAwait(false); - // Note: there is no "desiredName" when doing this. We're not going to do any - // renames of the user code. We're just looking for an extension method called - // "Select", but that name has no bearing on the code in question that we're - // trying to fix up. - var methodSymbols = OfType(symbols).SelectAsArray(s => s.WithDesiredName(null)); - var viableExtensionMethods = GetViableExtensionMethods(methodSymbols, type); + // Note: there is no "desiredName" when doing this. We're not going to do any renames of the user code. + // We're just looking for an extension method called "Select", but that name has no bearing on the code in + // question that we're trying to fix up. + var methodSymbols = OfType(symbols); + var namespaceSymbols = methodSymbols.SelectAsArray( + s => IsViableClassicExtensionMethod(s.Symbol, type, predicate), + s => s.WithDesiredName(null).WithSymbol(s.Symbol.ContainingNamespace)); - if (predicate != null) - { - viableExtensionMethods = viableExtensionMethods.WhereAsArray(s => predicate(s.Symbol)); - } + return GetNamespaceSymbolReferences(searchScope, namespaceSymbols); + } + + private bool IsViableClassicExtensionMethod( + IMethodSymbol? method, ITypeSymbol? receiver, Func? predicate) + { + if (receiver == null || method == null) + return false; - var namespaceSymbols = viableExtensionMethods.SelectAsArray(s => s.WithSymbol(s.Symbol.ContainingNamespace)); + if (!method.IsExtensionMethod) + return false; - return GetNamespaceSymbolReferences(searchScope, namespaceSymbols); + // It's possible that the 'method' we're looking at is from a different language than + // the language we're currently in. For example, we might find the extension method + // in an unreferenced VB project while we're in C#. However, in order to 'reduce' + // the extension method, the compiler requires both the method and receiver to be + // from the same language. + // + // So, if they're not from the same language, we simply can't proceed. Now in this + // case we decide that the method is not viable. But we could, in the future, decide + // to just always consider such methods viable. + + if (receiver.Language != method.Language) + return false; + + if (!method.IsAccessibleWithin(_semanticModel.Compilation.Assembly)) + return false; + + var reducedMethod = method.ReduceExtensionMethod(receiver); + if (reducedMethod is null) + return false; + + return predicate?.Invoke(method) is not false; + } + + private bool IsViableModernExtensionMember( + ISymbol? member, ITypeSymbol? receiver) + { + if (member is null || receiver is null) + return false; + + if (member is INamedTypeSymbol) + return false; + + if (!member.ContainingType.IsExtension) + return false; + + if (member.ContainingType.ExtensionParameter is not { Type: { } extensionParameterType }) + return false; + + if (!member.IsAccessibleWithin(_semanticModel.Compilation.Assembly)) + return false; + + // TODO: https://github.com/dotnet/roslyn/issues/80273 + // There is not api yet to know for certain if a modern extension is compatible with a receiver type. + // For now, put in a poor man's approach for this. + + receiver = receiver.OriginalDefinition; + extensionParameterType = extensionParameterType.OriginalDefinition; + + var conversion = _semanticModel.Compilation.ClassifyCommonConversion(receiver, extensionParameterType); + return conversion.Exists && conversion.IsImplicit; } private bool ExpressionBinds( - TSimpleNameSyntax nameNode, bool checkForExtensionMethods, CancellationToken cancellationToken) + TSimpleNameSyntax nameNode, bool checkForExtensionMembers, CancellationToken cancellationToken) { // See if the name binds to something other then the error type. If it does, there's nothing further we need to do. // For extension methods, however, we will continue to search if there exists any better matched method. cancellationToken.ThrowIfCancellationRequested(); var symbolInfo = _semanticModel.GetSymbolInfo(nameNode, cancellationToken); - if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && !checkForExtensionMethods) + if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && !checkForExtensionMembers) { return true; } @@ -617,9 +666,6 @@ private ImmutableArray GetNamespaceSymbolReferences( } private static ImmutableArray> OfType(ImmutableArray> symbols) where T : ISymbol - { - return symbols.WhereAsArray(s => s.Symbol is T) - .SelectAsArray(s => s.WithSymbol((T)s.Symbol)); - } + => symbols.SelectAsArray(s => s.Symbol is T, s => s.WithSymbol((T)s.Symbol)); } } diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs index 54208449e14..5bc94ead090 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs @@ -36,7 +36,7 @@ internal async Task FindNugetOrReferenceAssemblyReferencesAsync( if (!_owner.CanAddImportForTypeOrNamespace(_diagnosticId, _node, out var nameNode)) return; - if (ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken)) + if (ExpressionBinds(nameNode, checkForExtensionMembers: false, cancellationToken)) return; var (typeQuery, namespaceQuery, inAttributeContext) = GetSearchQueries(nameNode); diff --git a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs index 09f95fc8091..a980bdf43ba 100644 --- a/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs +++ b/src/roslyn/src/Features/Core/Portable/AddImport/SymbolResult.cs @@ -67,7 +67,7 @@ public bool DesiredNameMatchesSourceName(Document document) } } - private readonly struct SymbolResult(string desiredName, TSimpleNameSyntax nameNode, T symbol, double weight) where T : ISymbol + private readonly struct SymbolResult(string? desiredName, TSimpleNameSyntax nameNode, T symbol, double weight) where T : ISymbol { // The symbol that matched the string being searched for. public readonly T Symbol = symbol; @@ -77,7 +77,7 @@ private readonly struct SymbolResult(string desiredName, TSimpleNameSyntax na public readonly double Weight = weight; // The desired name to change the user text to if this was a fuzzy (spell-checking) match. - public readonly string DesiredName = desiredName; + public readonly string? DesiredName = desiredName; // The node to convert to the desired name public readonly TSimpleNameSyntax NameNode = nameNode; @@ -85,7 +85,7 @@ private readonly struct SymbolResult(string desiredName, TSimpleNameSyntax na public SymbolResult WithSymbol(T2 symbol) where T2 : ISymbol => new(DesiredName, NameNode, symbol, Weight); - internal SymbolResult WithDesiredName(string desiredName) + internal SymbolResult WithDesiredName(string? desiredName) => new(desiredName, NameNode, Symbol, Weight); } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs index 34834da5aa1..a5d813b79a1 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; @@ -17,7 +18,7 @@ internal sealed class CodeFixCollection( ImmutableArray fixes, FixAllState? fixAllState, ImmutableArray supportedScopes, - Diagnostic firstDiagnostic) + ImmutableArray diagnostics) { public object Provider { get; } = provider; public TextSpan TextSpan { get; } = span; @@ -28,5 +29,15 @@ internal sealed class CodeFixCollection( /// public FixAllState? FixAllState { get; } = fixAllState; public ImmutableArray SupportedScopes { get; } = supportedScopes.NullToEmpty(); - public Diagnostic FirstDiagnostic { get; } = firstDiagnostic; + + /// + /// Diagnostics this collection of fixes can fix. This is guaranteed to be non-empty. + /// + public ImmutableArray Diagnostics { get; } = ThrowIfDefaultOrEmpty(diagnostics); + + private static ImmutableArray ThrowIfDefaultOrEmpty(ImmutableArray diagnostics) + { + Contract.ThrowIfTrue(diagnostics.IsDefaultOrEmpty); + return diagnostics; + } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs index 10c4eb96236..eb63f29395f 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs @@ -91,7 +91,7 @@ private static ImmutableArray GetConfigurations(Project project, IEnume ? new TopLevelConfigureCodeStyleOptionCodeAction(diagnostic, nestedActions.ToImmutable()) : nestedActions.Single(); - result.Add(new CodeFix(project, resultCodeAction, diagnostic)); + result.Add(new CodeFix(resultCodeAction, [diagnostic])); } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs index 3ae7001b1ec..3a3d8850198 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs @@ -68,7 +68,7 @@ private static ImmutableArray GetConfigurations(Project project, IEnume } var codeAction = new TopLevelConfigureSeverityCodeAction(diagnostic, nestedActions.ToImmutableAndFree()); - result.Add(new CodeFix(project, codeAction, diagnostic)); + result.Add(new CodeFix(codeAction, [diagnostic])); // Bulk configuration is only supported for analyzer diagnostics. if (!SuppressionHelpers.IsCompilerDiagnostic(diagnostic)) @@ -111,7 +111,7 @@ void AddBulkConfigurationCodeFixes(ImmutableArray diagnostics, strin } var codeAction = new TopLevelBulkConfigureSeverityCodeAction(nestedActions.ToImmutableAndFree(), category); - result.Add(new CodeFix(project, codeAction, diagnostics)); + result.Add(new CodeFix(codeAction, diagnostics)); } } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.cs deleted file mode 100644 index 1f087c2d009..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/AbstractFixAllCodeFixCodeAction.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.CodeFixes; - -/// -/// Fix all code action for a code action registered by a . -/// -internal abstract class AbstractFixAllCodeFixCodeAction : AbstractFixAllCodeAction -{ - private static readonly HashSet s_predefinedCodeFixProviderNames = GetPredefinedCodeFixProviderNames(); - - protected AbstractFixAllCodeFixCodeAction( - IFixAllState fixAllState, bool showPreviewChangesDialog) - : base(fixAllState, showPreviewChangesDialog) - { - } - - protected sealed override IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken) - => new FixAllContext((FixAllState)fixAllState, progressTracker, cancellationToken); - - protected sealed override bool IsInternalProvider(IFixAllState fixAllState) - { - var exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeFixProviderAttribute), false); - if (exportAttributes?.Any() == true) - { - var exportAttribute = (ExportCodeFixProviderAttribute)exportAttributes.First(); - return !string.IsNullOrEmpty(exportAttribute.Name) - && s_predefinedCodeFixProviderNames.Contains(exportAttribute.Name); - } - - return false; - } - - private static HashSet GetPredefinedCodeFixProviderNames() - { - var names = new HashSet(); - - var fields = typeof(PredefinedCodeFixProviderNames).GetTypeInfo().DeclaredFields; - foreach (var field in fields) - { - if (field.IsStatic) - { - names.Add((string)field.GetValue(null)!); - } - } - - return names; - } -} diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs deleted file mode 100644 index 0f0822011ea..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.CodeFixes; - -internal sealed partial class FixMultipleCodeAction( - IFixAllState fixAllState, - string title, - string computingFixWaitDialogMessage) : AbstractFixAllCodeFixCodeAction(fixAllState, showPreviewChangesDialog: false) -{ - public override string Title => title; - - internal override string Message => computingFixWaitDialogMessage; -} diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs index 954b4091e23..b309f44ceb0 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.FixAllDiagnosticProvider.cs @@ -44,20 +44,17 @@ public override async Task> GetDocumentDiagnosticsAsync( { var service = document.Project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetDiagnosticsForIdsAsync( - document.Project, [document.Id], _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); + document.Project, [document.Id], _diagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } public override async Task> GetDocumentSpanDiagnosticsAsync(Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) { - bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id); - var service = document.Project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetDiagnosticsForSpanAsync( - document, fixAllSpan, shouldIncludeDiagnostic, - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), - DiagnosticKind.All, cancellationToken).ConfigureAwait(false)); + document, fixAllSpan, DiagnosticIdFilter.Include(_diagnosticIds), + priority: null, DiagnosticKind.All, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } @@ -67,7 +64,7 @@ public override async Task> GetAllDiagnosticsAsync(Proje // Get all diagnostics for the entire project, including document diagnostics. var service = project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetDiagnosticsForIdsAsync( - project, documentIds: default, _diagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); + project, documentIds: default, _diagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } @@ -76,7 +73,7 @@ public override async Task> GetProjectDiagnosticsAsync(P // Get all no-location diagnostics for the project, doesn't include document diagnostics. var service = project.Solution.Services.GetRequiredService(); var diagnostics = Filter(await service.GetProjectDiagnosticsForIdsAsync( - project, _diagnosticIds, shouldIncludeAnalyzer: null, cancellationToken).ConfigureAwait(false)); + project, _diagnosticIds, AnalyzerFilter.All, cancellationToken).ConfigureAwait(false)); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId == null)); return await diagnostics.ToDiagnosticsAsync(project, cancellationToken).ConfigureAwait(false); } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs index c3ecf3a63bd..6504c01261e 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/CodeFixService.cs @@ -70,9 +70,9 @@ public CodeFixService( _configurationProvidersMap = new(() => GetConfigurationProvidersPerLanguageMap(configurationProviders)); } - private Func? GetShouldIncludeDiagnosticPredicate( + private DiagnosticIdFilter GetShouldIncludeDiagnosticPredicate( TextDocument document, - ICodeActionRequestPriorityProvider priorityProvider) + CodeActionRequestPriority? priority) { // For Normal or Low priority, we only need to execute analyzers which can report at least one fixable // diagnostic that can have a non-suppression/configuration fix. @@ -80,35 +80,30 @@ public CodeFixService( // For CodeActionPriorityRequest.High, we only run compiler analyzer, which always has fixable diagnostics, // so we can return a null predicate here to include all diagnostics. - if (!(priorityProvider.Priority is CodeActionRequestPriority.Default or CodeActionRequestPriority.Low)) - return null; + if (!(priority is CodeActionRequestPriority.Default or CodeActionRequestPriority.Low)) + return DiagnosticIdFilter.All; - var hasWorkspaceFixers = TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); + TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); + workspaceFixersMap ??= ImmutableDictionary>.Empty; var projectFixersMap = GetProjectFixers(document); - return id => - { - if (hasWorkspaceFixers && workspaceFixersMap!.ContainsKey(id)) - return true; - - return projectFixersMap.ContainsKey(id); - }; + return DiagnosticIdFilter.Include([.. workspaceFixersMap.Keys, .. projectFixersMap.Keys]); } public async Task GetMostSevereFixAsync( - TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken) + TextDocument document, TextSpan range, CodeActionRequestPriority? priority, CancellationToken cancellationToken) { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}"); ImmutableArray allDiagnostics; using (TelemetryLogging.LogBlockTimeAggregatedHistogram( - FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) + FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}.{nameof(GetMostSevereFixAsync)}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) { var service = document.Project.Solution.Services.GetRequiredService(); allDiagnostics = await service.GetDiagnosticsForSpanAsync( - document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), - priorityProvider, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); + document, range, GetShouldIncludeDiagnosticPredicate(document, priority), + priority, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); // NOTE(cyrusn): We do not include suppressed diagnostics here as they are effectively hidden from the // user in the editor. As far as the user is concerned, there is no squiggle for it and no lightbulb @@ -116,7 +111,7 @@ public CodeFixService( allDiagnostics = allDiagnostics.WhereAsArray(d => !d.IsSuppressed); } - var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priorityProvider.Priority, cancellationToken).ConfigureAwait(false); + var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priority, cancellationToken).ConfigureAwait(false); allDiagnostics = allDiagnostics.AddRange(copilotDiagnostics); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); @@ -160,7 +155,7 @@ public CodeFixService( await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, - priorityProvider, cancellationToken).ConfigureAwait(false)) + priority, cancellationToken).ConfigureAwait(false)) { // Stop at the result error we see. return collection; @@ -173,14 +168,14 @@ public CodeFixService( public async IAsyncEnumerable StreamFixesAsync( TextDocument document, TextSpan range, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, [EnumeratorCancellation] CancellationToken cancellationToken) { - using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}"); + using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}"); // We only need to compute suppression/configuration fixes when request priority is // 'CodeActionPriorityRequest.Lowest' or no priority was provided at all (so all providers should run). - var includeSuppressionFixes = priorityProvider.Priority is null or CodeActionRequestPriority.Lowest; + var includeSuppressionFixes = priority is null or CodeActionRequestPriority.Lowest; // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic // service to give back current diagnostics for the given span, and it will use that to get fixes. @@ -195,17 +190,17 @@ public async IAsyncEnumerable StreamFixesAsync( ImmutableArray diagnostics; using (TelemetryLogging.LogBlockTimeAggregatedHistogram( - FunctionId.CodeFix_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) + FunctionId.CodeFix_Summary, $"Pri{priority.GetPriorityInt()}.{nameof(IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync)}")) { var service = document.Project.Solution.Services.GetRequiredService(); diagnostics = await service.GetDiagnosticsForSpanAsync( - document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), - priorityProvider, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); + document, range, GetShouldIncludeDiagnosticPredicate(document, priority), + priority, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); if (!includeSuppressionFixes) diagnostics = diagnostics.WhereAsArray(d => !d.IsSuppressed); } - var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priorityProvider.Priority, cancellationToken).ConfigureAwait(false); + var copilotDiagnostics = await GetCopilotDiagnosticsAsync(document, range, priority, cancellationToken).ConfigureAwait(false); diagnostics = diagnostics.AddRange(copilotDiagnostics); if (diagnostics.IsEmpty) @@ -217,11 +212,11 @@ public async IAsyncEnumerable StreamFixesAsync( var spanToDiagnostics = ConvertToMap(text, diagnostics); // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. - if (priorityProvider.Priority != CodeActionRequestPriority.Lowest) + if (priority != CodeActionRequestPriority.Lowest) { await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, - priorityProvider, cancellationToken).ConfigureAwait(false)) + priority, cancellationToken).ConfigureAwait(false)) { yield return collection; } @@ -297,7 +292,7 @@ private static SortedDictionary> ConvertToMap( { var service = document.Project.Solution.Services.GetRequiredService(); diagnostics = await service.GetDiagnosticsForSpanAsync( - document, range, diagnosticId, priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + document, range, diagnosticId, priority: null, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); // NOTE(cyrusn): We do not include suppressed diagnostics here as they are effectively hidden from the @@ -317,7 +312,7 @@ private static SortedDictionary> ConvertToMap( }; await foreach (var collection in StreamFixesAsync( - document, spanToDiagnostics, fixAllForInSpan: true, new DefaultCodeActionRequestPriorityProvider(), + document, spanToDiagnostics, fixAllForInSpan: true, priority: null, cancellationToken).ConfigureAwait(false)) { if (collection.FixAllState is not null && collection.SupportedScopes.Contains(FixAllScope.Document)) @@ -443,7 +438,7 @@ private async IAsyncEnumerable StreamFixesAsync( TextDocument document, SortedDictionary> spanToDiagnostics, bool fixAllForInSpan, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, [EnumeratorCancellation] CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -500,13 +495,15 @@ private async IAsyncEnumerable StreamFixesAsync( if (TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) allFixers = allFixers.Sort(new FixerComparer(allFixers, fixersForLanguage.Value)); - var extensionManager = document.Project.Solution.Services.GetService(); + var solution = document.Project.Solution; + var extensionManager = solution.Services.GetService(); + var diagnosticAnalyzerService = solution.Services.GetRequiredService(); // Run each CodeFixProvider to gather individual CodeFixes for reported diagnostics. // Ensure that no diagnostic has registered code actions from different code fix providers with same equivalance key. // This prevents duplicate registered code actions from NuGet and VSIX code fix providers. // See https://github.com/dotnet/roslyn/issues/18818 for details. - var uniqueDiagosticToEquivalenceKeysMap = new Dictionary>(); + var uniqueDiagnosticToEquivalenceKeysMap = new Dictionary>(); // NOTE: For backward compatibility, we allow multiple registered code actions from the same code fix provider // to have the same equivalence key. See https://github.com/dotnet/roslyn/issues/44553 for details. @@ -519,7 +516,7 @@ private async IAsyncEnumerable StreamFixesAsync( { cancellationToken.ThrowIfCancellationRequested(); - if (!priorityProvider.MatchesPriority(fixer)) + if (!await MatchesPriorityAsync(fixer).ConfigureAwait(false)) continue; foreach (var (span, diagnostics) in fixerToRangesAndDiagnostics[fixer]) @@ -553,13 +550,13 @@ private async IAsyncEnumerable StreamFixesAsync( { var primaryDiagnostic = dxs.First(); return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, fixerMetadata, - [primaryDiagnostic], uniqueDiagosticToEquivalenceKeysMap, + [primaryDiagnostic], uniqueDiagnosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); } else { return GetCodeFixesAsync(document, span, fixer, fixerMetadata, dxs, - uniqueDiagosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); + uniqueDiagnosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); } } }, @@ -578,7 +575,7 @@ private async IAsyncEnumerable StreamFixesAsync( } finally { - foreach (var pooledSet in uniqueDiagosticToEquivalenceKeysMap.Values) + foreach (var pooledSet in uniqueDiagnosticToEquivalenceKeysMap.Values) { pooledSet.Free(); } @@ -599,6 +596,41 @@ static void AddAllFixers( fixerToRangesAndDiagnostics.MultiAdd(fixer, (range, diagnostics)); } } + + // + // Returns true if the given should be considered a candidate when computing + // fixes for the given . + // + async Task MatchesPriorityAsync(CodeFixProvider codeFixProvider) + { + if (priority == null) + { + // We are computing fixes for all priorities + return true; + } + + if (priority == codeFixProvider.RequestPriority) + { + return true; + } + + if (priority == CodeActionRequestPriority.Low && + codeFixProvider.RequestPriority > CodeActionRequestPriority.Low && + await diagnosticAnalyzerService.IsAnyDiagnosticIdDeprioritizedAsync( + document.Project, codeFixProvider.FixableDiagnosticIds, cancellationToken).ConfigureAwait(false)) + { + // 'Low' priority can be used for two types of code fixers: + // 1. Those which explicitly set their 'RequestPriority' to 'Low' and + // 2. Those which can fix diagnostics for expensive analyzers which were de-prioritized + // to 'Low' priority bucket to improve lightbulb population performance. + // The first case is handled by the earlier check against matching priorities. For the second + // case, we accept fixers with any RequestPriority, as long as they can fix a diagnostic from + // an analyzer that was executed in the 'Low' bucket. + return true; + } + + return false; + } } private CodeChangeProviderMetadata? TryGetMetadata(CodeFixProvider fixer) @@ -650,7 +682,7 @@ private static async Task> GetCodeFixesAsync( // name metadata. action.AddCustomTagAndTelemetryInfo(fixerMetadata, fixer); - fixes.Add(new CodeFix(document.Project, action, applicableDiagnostics)); + fixes.Add(new CodeFix(action, applicableDiagnostics)); } } }, @@ -750,15 +782,14 @@ private async IAsyncEnumerable StreamConfigurationFixesAsync( { cancellationToken.ThrowIfCancellationRequested(); - var allDiagnostics = - await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) - .ToDiagnosticsAsync(textDocument.Project, cancellationToken).ConfigureAwait(false); + var allDiagnostics = await diagnosticsWithSameSpan + .OrderByDescending(d => d.Severity) + .ToDiagnosticsAsync(textDocument.Project, cancellationToken).ConfigureAwait(false); var diagnostics = allDiagnostics.WhereAsArray(hasFix); - if (diagnostics.Length <= 0) - { - // this can happen for suppression case where all diagnostics can't be suppressed + + // this can happen for suppression case where all diagnostics can't be suppressed + if (diagnostics.IsEmpty) return null; - } var extensionManager = textDocument.Project.Solution.Services.GetRequiredService(); var fixes = await extensionManager.PerformFunctionAsync(fixer, @@ -776,13 +807,10 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) var supportedScopes = ImmutableArray.Empty; if (fixAllProviderInfo != null && textDocument is Document document) { - var diagnosticIds = diagnostics.Where(fixAllProviderInfo.CanBeFixed) - .Select(d => d.Id) - .ToImmutableHashSet(); - - var diagnosticProvider = fixAllForInSpan - ? new FixAllPredefinedDiagnosticProvider(allDiagnostics) - : (FixAllContext.DiagnosticProvider)new FixAllDiagnosticProvider(diagnosticIds); + var diagnosticIds = diagnostics + .Where(fixAllProviderInfo.CanBeFixed) + .Select(d => d.Id) + .ToImmutableHashSet(); var codeFixProvider = (fixer as CodeFixProvider) ?? new WrapperCodeFixProvider((IConfigurationFixProvider)fixer, diagnostics.Select(d => d.Id)); @@ -795,14 +823,15 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) FixAllScope.Document, fixes[0].Action.EquivalenceKey, diagnosticIds, - diagnosticProvider); + fixAllForInSpan + ? new FixAllPredefinedDiagnosticProvider(allDiagnostics) + : new FixAllDiagnosticProvider(diagnosticIds)); supportedScopes = fixAllProviderInfo.SupportedScopes; } return new CodeFixCollection( - fixer, fixesSpan, fixes, fixAllState, - supportedScopes, diagnostics.First()); + fixer, fixesSpan, fixes, fixAllState, supportedScopes, diagnostics); } /// Looks explicitly for an . diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs index 9c163339f49..61776b70b2c 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Service/ICodeFixService.cs @@ -14,14 +14,14 @@ namespace Microsoft.CodeAnalysis.CodeFixes; internal interface ICodeFixService { - IAsyncEnumerable StreamFixesAsync(TextDocument document, TextSpan textSpan, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken); + IAsyncEnumerable StreamFixesAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority? priority, CancellationToken cancellationToken); /// /// Similar to except that instead of streaming all results, this ends with the /// first. This will also attempt to return a fix for an error first, but will fall back to any fix if that /// does not succeed. /// - Task GetMostSevereFixAsync(TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken); + Task GetMostSevereFixAsync(TextDocument document, TextSpan range, CodeActionRequestPriority? priority, CancellationToken cancellationToken); Task GetDocumentFixAllForIdInSpanAsync(TextDocument document, TextSpan textSpan, string diagnosticId, DiagnosticSeverity severity, CancellationToken cancellationToken); Task ApplyCodeFixesForSpecificDiagnosticIdAsync(TDocument document, string diagnosticId, DiagnosticSeverity severity, IProgress progressTracker, CancellationToken cancellationToken) @@ -32,11 +32,11 @@ Task ApplyCodeFixesForSpecificDiagnosticIdAsync(TDocument internal static class ICodeFixServiceExtensions { public static IAsyncEnumerable StreamFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, range, new DefaultCodeActionRequestPriorityProvider(), cancellationToken); + => service.StreamFixesAsync(document, range, priority: null, cancellationToken); public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CancellationToken cancellationToken) => service.StreamFixesAsync(document, range, cancellationToken).ToImmutableArrayAsync(cancellationToken); - public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan textSpan, ICodeActionRequestPriorityProvider priorityProvider, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, textSpan, priorityProvider, cancellationToken).ToImmutableArrayAsync(cancellationToken); + public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan textSpan, CodeActionRequestPriority? priority, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, textSpan, priority, cancellationToken).ToImmutableArrayAsync(cancellationToken); } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs index 964117f5ba1..e5e5b8521de 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs @@ -235,16 +235,14 @@ private async Task> GetSuppressionsAsync( { var codeAction = new TopLevelSuppressionCodeAction( diagnostic, nestedActions.ToImmutableAndClear()); - result.Add(new CodeFix(project, codeAction, diagnostic)); + result.Add(new CodeFix(codeAction, [diagnostic])); } } else if (!skipUnsuppress) { var codeAction = await RemoveSuppressionCodeAction.CreateAsync(suppressionTargetInfo, documentOpt, project, diagnostic, this, cancellationToken).ConfigureAwait(false); if (codeAction != null) - { - result.Add(new CodeFix(project, codeAction, diagnostic)); - } + result.Add(new CodeFix(codeAction, [diagnostic])); } } diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs index 7d712ec0dab..44cc9d1e5cc 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs @@ -3,11 +3,16 @@ // 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.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; @@ -15,49 +20,61 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// Fix all code action for a code action registered by /// a or a . /// -internal abstract class AbstractFixAllCodeAction( - IFixAllState fixAllState, bool showPreviewChangesDialog) : CodeAction +internal sealed class RefactorOrFixAllCodeAction( + IRefactorOrFixAllState refactorOrFixAllState, + bool showPreviewChangesDialog, + string? title = null, + string? message = null) : CodeAction { + private static readonly ISet s_predefinedProviderNames = + typeof(PredefinedCodeFixProviderNames).GetTypeInfo().DeclaredFields.Concat(typeof(PredefinedCodeRefactoringProviderNames).GetTypeInfo().DeclaredFields) + .Where(field => field.IsStatic) + .Select(field => (string)field.GetValue(null)!) + .ToSet(); + private bool _showPreviewChangesDialog = showPreviewChangesDialog; - public IFixAllState FixAllState { get; } = fixAllState; + public IRefactorOrFixAllState RefactorOrFixAllState { get; } = refactorOrFixAllState; // We don't need to post process changes here as the inner code action created for Fix multiple code fix already executes. internal sealed override CodeActionCleanup Cleanup => CodeActionCleanup.None; /// - /// Determine if the is an internal first-party provider or not. + /// Creates a new with the given parameters. /// - protected abstract bool IsInternalProvider(IFixAllState fixAllState); - - /// - /// Creates a new with the given parameters. - /// - protected abstract IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken); + private static IRefactorOrFixAllContext CreateFixAllContext(IRefactorOrFixAllState state, IProgress progressTracker, CancellationToken cancellationToken) + { + return state switch + { + FixAllState fixAllState => new FixAllContext(fixAllState, progressTracker, cancellationToken), + RefactorAllState refactorAllState => new RefactorAllContext(refactorAllState, progressTracker, cancellationToken), + _ => throw ExceptionUtilities.UnexpectedValue(state), + }; + } public override string Title - => this.FixAllState.Scope switch + => title ?? (this.RefactorOrFixAllState.Scope switch { FixAllScope.Document => FeaturesResources.Document, FixAllScope.Project => FeaturesResources.Project, FixAllScope.Solution => FeaturesResources.Solution, FixAllScope.ContainingMember => FeaturesResources.Containing_Member, FixAllScope.ContainingType => FeaturesResources.Containing_Type, - _ => throw ExceptionUtilities.UnexpectedValue(this.FixAllState.Scope), - }; + _ => throw ExceptionUtilities.UnexpectedValue(this.RefactorOrFixAllState.Scope), + }); - internal override string Message => FeaturesResources.Computing_fix_all_occurrences_code_fix; + internal override string Message => message ?? FeaturesResources.Computing_fix_all_occurrences_code_fix; protected sealed override Task> ComputeOperationsAsync( IProgress progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - FixAllLogger.LogState(FixAllState, IsInternalProvider(FixAllState)); + FixAllLogger.LogState(RefactorOrFixAllState, IsInternalProvider(RefactorOrFixAllState)); - var service = FixAllState.Project.Solution.Services.GetRequiredService(); + var service = RefactorOrFixAllState.Project.Solution.Services.GetRequiredService(); - var fixAllContext = CreateFixAllContext(FixAllState, progressTracker, cancellationToken); - progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultFixAllTitle())); + var fixAllContext = CreateFixAllContext(RefactorOrFixAllState, progressTracker, cancellationToken); + progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultTitle())); return service.GetFixAllOperationsAsync(fixAllContext, _showPreviewChangesDialog); } @@ -66,16 +83,38 @@ protected sealed override Task> ComputeOpera IProgress progressTracker, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - FixAllLogger.LogState(FixAllState, IsInternalProvider(FixAllState)); + FixAllLogger.LogState(RefactorOrFixAllState, IsInternalProvider(RefactorOrFixAllState)); - var service = FixAllState.Project.Solution.Services.GetRequiredService(); + var service = RefactorOrFixAllState.Project.Solution.Services.GetRequiredService(); - var fixAllContext = CreateFixAllContext(FixAllState, progressTracker, cancellationToken); - progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultFixAllTitle())); + var fixAllContext = CreateFixAllContext(RefactorOrFixAllState, progressTracker, cancellationToken); + progressTracker.Report(CodeAnalysisProgress.Description(fixAllContext.GetDefaultTitle())); return service.GetFixAllChangedSolutionAsync(fixAllContext); } + /// + /// Determine if the is an internal first-party provider or not. + /// + private static bool IsInternalProvider(IRefactorOrFixAllState fixAllState) + { + var exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeFixProviderAttribute), false); + if (exportAttributes?.FirstOrDefault() is ExportCodeFixProviderAttribute codeFixAttribute) + { + return !string.IsNullOrEmpty(codeFixAttribute.Name) + && s_predefinedProviderNames.Contains(codeFixAttribute.Name); + } + + exportAttributes = fixAllState.Provider.GetType().GetTypeInfo().GetCustomAttributes(typeof(ExportCodeRefactoringProviderAttribute), false); + if (exportAttributes?.FirstOrDefault() is ExportCodeRefactoringProviderAttribute codeRefactoringAttribute) + { + return !string.IsNullOrEmpty(codeRefactoringAttribute.Name) + && s_predefinedProviderNames.Contains(codeRefactoringAttribute.Name); + } + + return false; + } + // internal for testing purposes. internal TestAccessor GetTestAccessor() => new(this); @@ -83,9 +122,9 @@ internal TestAccessor GetTestAccessor() // internal for testing purposes. internal readonly struct TestAccessor { - private readonly AbstractFixAllCodeAction _fixAllCodeAction; + private readonly RefactorOrFixAllCodeAction _fixAllCodeAction; - internal TestAccessor(AbstractFixAllCodeAction fixAllCodeAction) + internal TestAccessor(RefactorOrFixAllCodeAction fixAllCodeAction) => _fixAllCodeAction = fixAllCodeAction; /// diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs index c503e2c8be3..97b18de47ba 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs @@ -22,7 +22,7 @@ internal abstract class AbstractFixAllGetFixesService : IFixAllGetFixesService string fixAllTopLevelHeader, Glyph glyph); - public async Task GetFixAllChangedSolutionAsync(IFixAllContext fixAllContext) + public async Task GetFixAllChangedSolutionAsync(IRefactorOrFixAllContext fixAllContext) { var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); if (codeAction == null) @@ -35,7 +35,7 @@ internal abstract class AbstractFixAllGetFixesService : IFixAllGetFixesService } public async Task> GetFixAllOperationsAsync( - IFixAllContext fixAllContext, bool showPreviewChangesDialog) + IRefactorOrFixAllContext fixAllContext, bool showPreviewChangesDialog) { var codeAction = await GetFixAllCodeActionAsync(fixAllContext).ConfigureAwait(false); if (codeAction == null) @@ -51,7 +51,7 @@ private async Task> GetFixAllOperationsAsync CodeAction codeAction, bool showPreviewChangesDialog, IProgress progressTracker, - IFixAllState fixAllState, + IRefactorOrFixAllState fixAllState, CancellationToken cancellationToken) { // We have computed the fix all occurrences code fix. @@ -153,7 +153,7 @@ private async Task> GetFixAllOperationsAsync } } - private static async Task GetFixAllCodeActionAsync(IFixAllContext fixAllContext) + private static async Task GetFixAllCodeActionAsync(IRefactorOrFixAllContext fixAllContext) { var fixAllKind = fixAllContext.State.FixAllKind; var functionId = fixAllKind switch @@ -175,7 +175,7 @@ private async Task> GetFixAllOperationsAsync CodeAction? action = null; try { - action = await fixAllContext.State.FixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + action = await fixAllContext.State.FixAllProvider.GetCodeActionAsync(fixAllContext).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs deleted file mode 100644 index cdb4e5550f8..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.CodeActions; - -internal interface ICodeActionRequestPriorityProvider -{ - /// - /// represents no specified priority. i.e. any priority should match this. - /// - CodeActionRequestPriority? Priority { get; } - - /// - /// Tracks the given as a de-prioritized analyzer that should be moved to - /// bucket. - /// - void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer); - - bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer); - - /// - /// Indicates whether any deprioritized analyzer supports one of the passed in diagnostic ids. - /// - bool HasDeprioritizedAnalyzerSupportingDiagnosticId(ImmutableArray diagnosticIds); -} - -internal static class ICodeActionRequestPriorityProviderExtensions -{ - /// - /// Returns true if the given can report diagnostics that can have fixes from a code - /// fix provider with matching . This method is useful for performing a performance - /// optimization for lightbulb diagnostic computation, wherein we can reduce the set of analyzers to be executed - /// when computing fixes for a specific . - /// - public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, DiagnosticAnalyzer analyzer) - { - var priority = provider.Priority; - - // If caller isn't asking for prioritized result, then run all analyzers. - if (priority is null) - return true; - - // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, - // which requires all analyzer diagnostics. - if (priority == CodeActionRequestPriority.Lowest) - return true; - - // The compiler analyzer always counts for any priority. It's diagnostics may be fixed - // by high pri or normal pri fixers. - if (analyzer.IsCompilerAnalyzer()) - return true; - - // Check if we are computing diagnostics for 'CodeActionRequestPriority.Low' and - // this analyzer was de-prioritized to low priority bucket. - if (priority == CodeActionRequestPriority.Low && - provider.IsDeprioritizedAnalyzerWithLowPriority(analyzer)) - { - return true; - } - - // Now compute this analyzer's priority and compare it with the provider's request 'Priority'. - // Our internal 'IBuiltInAnalyzer' can specify custom request priority, while all - // the third-party analyzers are assigned 'Medium' priority. - var analyzerPriority = analyzer is IBuiltInAnalyzer { IsHighPriority: true } - ? CodeActionRequestPriority.High - : CodeActionRequestPriority.Default; - - return priority == analyzerPriority; - } - - /// - /// Returns true if the given should be considered a candidate when computing - /// fixes for the given . - /// - public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, CodeFixProvider codeFixProvider) - { - if (provider.Priority == null) - { - // We are computing fixes for all priorities - return true; - } - - if (provider.Priority == codeFixProvider.RequestPriority) - { - return true; - } - - if (provider.Priority == CodeActionRequestPriority.Low - && provider.HasDeprioritizedAnalyzerSupportingDiagnosticId(codeFixProvider.FixableDiagnosticIds) - && codeFixProvider.RequestPriority > CodeActionRequestPriority.Low) - { - // 'Low' priority can be used for two types of code fixers: - // 1. Those which explicitly set their 'RequestPriority' to 'Low' and - // 2. Those which can fix diagnostics for expensive analyzers which were de-prioritized - // to 'Low' priority bucket to improve lightbulb population performance. - // The first case is handled by the earlier check against matching priorities. For the second - // case, we accept fixers with any RequestPriority, as long as they can fix a diagnostic from - // an analyzer that was executed in the 'Low' bucket. - return true; - } - - return false; - } -} - -internal sealed class DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority? priority = null) : ICodeActionRequestPriorityProvider -{ - private readonly object _gate = new(); - private HashSet? _lowPriorityAnalyzers; - private HashSet? _lowPriorityAnalyzerSupportedDiagnosticIds; - - public CodeActionRequestPriority? Priority { get; } = priority; - - public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - { - lock (_gate) - { - _lowPriorityAnalyzers ??= []; - _lowPriorityAnalyzerSupportedDiagnosticIds ??= []; - - _lowPriorityAnalyzers.Add(analyzer); - - foreach (var supportedDiagnostic in analyzer.SupportedDiagnostics) - _lowPriorityAnalyzerSupportedDiagnosticIds.Add(supportedDiagnostic.Id); - } - } - - public bool HasDeprioritizedAnalyzerSupportingDiagnosticId(ImmutableArray diagnosticIds) - { - lock (_gate) - { - if (_lowPriorityAnalyzerSupportedDiagnosticIds == null) - return false; - - foreach (var diagnosticId in diagnosticIds) - { - if (_lowPriorityAnalyzerSupportedDiagnosticIds.Contains(diagnosticId)) - return true; - } - - return false; - } - } - - public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) - { - lock (_gate) - { - return _lowPriorityAnalyzers != null && _lowPriorityAnalyzers.Contains(analyzer); - } - } -} diff --git a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs index 2f802c41053..da9494aa5bc 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeFixesAndRefactorings/IFixAllGetFixesService.cs @@ -16,12 +16,12 @@ internal interface IFixAllGetFixesService : IWorkspaceService /// Computes the fix all occurrences code fix, brings up the preview changes dialog for the fix and /// returns the code action operations corresponding to the fix. /// - Task> GetFixAllOperationsAsync(IFixAllContext fixAllContext, bool showPreviewChangesDialog); + Task> GetFixAllOperationsAsync(IRefactorOrFixAllContext fixAllContext, bool showPreviewChangesDialog); /// /// Computes the fix all occurrences code fix and returns the changed solution. /// - Task GetFixAllChangedSolutionAsync(IFixAllContext fixAllContext); + Task GetFixAllChangedSolutionAsync(IRefactorOrFixAllContext fixAllContext); /// /// Previews the changes that would occur after a code fix and returns the updated solution with those changes. diff --git a/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs b/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs index 57d69469161..7ed457339fd 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs @@ -30,7 +30,7 @@ internal sealed class CodeLensFindReferencesProgress( private readonly CancellationTokenSource _aggregateCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); private readonly SyntaxNode _queriedNode = queriedNode; private readonly ISymbol _queriedSymbol = queriedDefinition; - private readonly ConcurrentSet _locations = new ConcurrentSet(LocationComparer.Instance); + private readonly ConcurrentSet _locations = new(LocationComparer.Instance); /// /// If the cap is 0, then there is no cap. diff --git a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 07a2d4022da..0d6bff46bed 100644 --- a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/roslyn/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -31,7 +31,7 @@ internal sealed class CodeRefactoringService( [ImportMany] IEnumerable> providers) : ICodeRefactoringService { private readonly Lazy>>> _lazyLanguageDocumentToProvidersMap = - new Lazy>>>(() => + new(() => ImmutableDictionary.CreateRange( DistributeLanguagesAndDocuments(providers) .GroupBy(lz => new ProviderKey(lz.Metadata.Language, lz.Metadata.DocumentKind, lz.Metadata.DocumentExtension)) diff --git a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs b/src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs deleted file mode 100644 index 9ce8a3ab1c2..00000000000 --- a/src/roslyn/src/Features/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllCodeRefactoringCodeAction.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.CodeRefactorings; - -/// -/// Fix all code action for a code action registered by a . -/// -internal sealed class FixAllCodeRefactoringCodeAction(IFixAllState fixAllState) : AbstractFixAllCodeAction(fixAllState, showPreviewChangesDialog: true) -{ - protected override IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken) - => new FixAllContext((FixAllState)fixAllState, progressTracker, cancellationToken); - - protected override bool IsInternalProvider(IFixAllState fixAllState) - => true; // FixAll for refactorings is currently only supported for internal code refactoring providers. -} diff --git a/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs b/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs index 4bf683611ab..3bed61c9165 100644 --- a/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs @@ -28,7 +28,7 @@ internal abstract class AbstractDocCommentCompletionProvider : LSPCompl private static readonly ImmutableArray s_topLevelSingleUseTagNames = [SummaryElementName, RemarksElementName, ExampleElementName, CompletionListElementName]; private static readonly Dictionary s_tagMap = - new Dictionary() + new() { // tagOpen textBeforeCaret $$ textAfterCaret tagClose { ExceptionElementName, ($"<{ExceptionElementName}", $" {CrefAttributeName}=\"", "\"", null) }, diff --git a/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs b/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs index 81367f86ad2..273b7f75d3d 100644 --- a/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs @@ -10,13 +10,12 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; internal abstract class AbstractObjectInitializerCompletionProvider : LSPCompletionProvider { - protected abstract Tuple? GetInitializedType(Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken); + protected abstract (ITypeSymbol type, Location location, bool isObjectInitializer)? GetInitializedType(Document document, SemanticModel semanticModel, int position, CancellationToken cancellationToken); protected abstract HashSet GetInitializedMembers(SyntaxTree tree, int position, CancellationToken cancellationToken); protected abstract string EscapeIdentifier(ISymbol symbol); @@ -27,7 +26,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var cancellationToken = context.CancellationToken; var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); - if (GetInitializedType(document, semanticModel, position, cancellationToken) is not var (type, initializerLocation)) + if (GetInitializedType(document, semanticModel, position, cancellationToken) is not var (type, initializerLocation, isObjectInitializer)) return; if (type is ITypeParameterSymbol typeParameterSymbol) @@ -64,7 +63,9 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) // We'll hard select the first required member to make it a bit easier to type out an object initializer // with a bunch of members. - if (firstUninitializedRequiredMember && uninitializedMember.IsRequired()) + if (firstUninitializedRequiredMember && + isObjectInitializer && + uninitializedMember.IsRequired()) { rules = rules.WithSelectionBehavior(CompletionItemSelectionBehavior.HardSelection).WithMatchPriority(MatchPriority.Preselect); firstUninitializedRequiredMember = false; @@ -76,7 +77,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) insertionText: null, symbols: [uninitializedMember], contextPosition: initializerLocation.SourceSpan.Start, - inlineDescription: uninitializedMember.IsRequired() ? FeaturesResources.Required : null, + inlineDescription: isObjectInitializer && uninitializedMember.IsRequired() ? FeaturesResources.Required : null, rules: rules)); } } diff --git a/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs b/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs index 90708a1b727..7b0036ec537 100644 --- a/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs +++ b/src/roslyn/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionCacheEntry.cs @@ -35,7 +35,7 @@ public sealed class Builder(Checksum checksum, string langauge, IEqualityCompare private readonly Checksum _checksum = checksum; private readonly string _language = langauge; - private readonly MultiDictionary _mapBuilder = new MultiDictionary(comparer); + private readonly MultiDictionary _mapBuilder = new(comparer); public ExtensionMethodImportCompletionCacheEntry ToCacheEntry() { diff --git a/src/roslyn/src/Features/Core/Portable/Completion/Providers/SymbolMatchPriority.cs b/src/roslyn/src/Features/Core/Portable/Completion/Providers/SymbolMatchPriority.cs index 206b744d1a9..79b1bfc54b0 100644 --- a/src/roslyn/src/Features/Core/Portable/Completion/Providers/SymbolMatchPriority.cs +++ b/src/roslyn/src/Features/Core/Portable/Completion/Providers/SymbolMatchPriority.cs @@ -14,4 +14,7 @@ internal static class SymbolMatchPriority internal static readonly int PreferEventOrMethod = 400; internal static readonly int PreferFieldOrProperty = 500; internal static readonly int PreferLocalOrParameterOrRangeVariable = 600; + + // For keywords like 'ref' that the language requires at certain locations. + internal static readonly int PreferRequiredKeyword = 700; } diff --git a/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs index 2723f8b73cf..0e7935145c2 100644 --- a/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.cs @@ -29,7 +29,7 @@ internal abstract partial class AbstractConvertIfToSwitchCodeRefactoringProvider public abstract string GetTitle(bool forSwitchExpression); public abstract Analyzer CreateAnalyzer(ISyntaxFacts syntaxFacts, ParseOptions options); - protected sealed override ImmutableArray SupportedFixAllScopes => AllFixAllScopes; + protected sealed override ImmutableArray SupportedRefactorAllScopes => AllRefactorAllScopes; public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -178,7 +178,7 @@ static bool CanConvertSectionForSwitchExpression(bool supportsOrPattern, Analyze } } - protected sealed override async Task FixAllAsync( + protected sealed override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs index f6dd0b180b2..fbbbad54f74 100644 --- a/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs @@ -47,7 +47,7 @@ private readonly record struct InvocationData( protected abstract TExpressionSyntax ParseExpression(string text); - protected override ImmutableArray SupportedFixAllScopes { get; } = AllFixAllScopes; + protected override ImmutableArray SupportedRefactorAllScopes { get; } = AllRefactorAllScopes; private InvocationData? AnalyzeInvocation( Document document, @@ -251,7 +251,7 @@ bool ContainsIndex(string stringLiteralText, string indexString) } } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, diff --git a/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs b/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs index c506ff37ff1..ca2d258887a 100644 --- a/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs +++ b/src/roslyn/src/Features/Core/Portable/Copilot/ICopilotChangeAnalysisService.cs @@ -284,7 +284,7 @@ await firstAction await foreach (var (codeFixCollection, success, applicationTime) in values.ConfigureAwait(false)) { - var diagnosticId = codeFixCollection.FirstDiagnostic.Id; + var diagnosticId = codeFixCollection.Diagnostics.First().Id; var providerName = GetProviderName(codeFixCollection); IncrementCount(diagnosticIdToCount, diagnosticId); @@ -350,9 +350,9 @@ static async (span, callback, args, cancellationToken) => if (codeFixCollection is { Provider: not IConfigurationFixProvider, - Fixes: [var codeFix, ..], + Fixes: [{ Diagnostics: [var primaryDiagnostic, ..] }, ..], } && - IsVisibleDiagnostic(codeFix.PrimaryDiagnostic.IsSuppressed, codeFix.PrimaryDiagnostic.Severity) && + IsVisibleDiagnostic(primaryDiagnostic.IsSuppressed, primaryDiagnostic.Severity) && (codeFixCollection.Provider.GetType().Namespace ?? "").StartsWith(RoslynPrefix)) { callback(codeFixCollection); diff --git a/src/roslyn/src/Features/Core/Portable/Copilot/IProposalAdjusterService.cs b/src/roslyn/src/Features/Core/Portable/Copilot/IProposalAdjusterService.cs index 1e2e8731498..84df0158548 100644 --- a/src/roslyn/src/Features/Core/Portable/Copilot/IProposalAdjusterService.cs +++ b/src/roslyn/src/Features/Core/Portable/Copilot/IProposalAdjusterService.cs @@ -6,11 +6,13 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddMissingImports; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -18,26 +20,38 @@ namespace Microsoft.CodeAnalysis.Copilot; -internal interface ICopilotProposalAdjusterService : IWorkspaceService +internal static class ProposalAdjusterKinds +{ + public const string AddMissingImports = nameof(AddMissingImports); + public const string AddMissingTokens = nameof(AddMissingTokens); +} + +[DataContract] +internal readonly record struct ProposalAdjustmentResult( + [property: DataMember(Order = 0)] ImmutableArray TextChanges, + [property: DataMember(Order = 1)] bool Format, + [property: DataMember(Order = 2)] ImmutableArray AdjustmentKinds); + +internal interface ICopilotProposalAdjusterService : ILanguageService { /// default if the proposal was not adjusted - ValueTask> TryAdjustProposalAsync( + ValueTask TryAdjustProposalAsync( Document document, ImmutableArray normalizedChanges, CancellationToken cancellationToken); } internal interface IRemoteCopilotProposalAdjusterService { /// - ValueTask> TryAdjustProposalAsync( + ValueTask TryAdjustProposalAsync( Checksum solutionChecksum, DocumentId documentId, ImmutableArray normalizedChanges, CancellationToken cancellationToken); } -[ExportWorkspaceService(typeof(ICopilotProposalAdjusterService), ServiceLayer.Default), Shared] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class DefaultCopilotProposalAdjusterService() : ICopilotProposalAdjusterService +internal abstract class AbstractCopilotProposalAdjusterService() : ICopilotProposalAdjusterService { - public async ValueTask> TryAdjustProposalAsync( + protected abstract Task> AddMissingTokensIfAppropriateAsync( + Document originalDocument, ImmutableArray normalizedChanges, CancellationToken cancellationToken); + + public async ValueTask TryAdjustProposalAsync( Document document, ImmutableArray normalizedChanges, CancellationToken cancellationToken) { if (normalizedChanges.IsDefaultOrEmpty) @@ -46,7 +60,7 @@ public async ValueTask> TryAdjustProposalAsync( var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var result = await client.TryInvokeAsync>( + var result = await client.TryInvokeAsync( document.Project, (service, checksum, cancellationToken) => service.TryAdjustProposalAsync(checksum, document.Id, normalizedChanges, cancellationToken), cancellationToken).ConfigureAwait(false); @@ -59,7 +73,7 @@ public async ValueTask> TryAdjustProposalAsync( } } - private static async Task> TryAdjustProposalInCurrentProcessAsync( + private async Task TryAdjustProposalInCurrentProcessAsync( Document originalDocument, ImmutableArray normalizedChanges, CancellationToken cancellationToken) { CopilotUtilities.ThrowIfNotNormalized(normalizedChanges); @@ -68,6 +82,19 @@ private static async Task> TryAdjustProposalInCurrent // move to in the forked doucment, as that is what we will want to analyze. var oldText = await originalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + var changesWhenMissingTokens = await AddMissingTokensIfAppropriateAsync( + originalDocument, normalizedChanges, cancellationToken).ConfigureAwait(false); + + using var _ = ArrayBuilder.GetInstance(out var adjustmentKinds); + + var format = false; + if (!changesWhenMissingTokens.IsDefault) + { + adjustmentKinds.Add(ProposalAdjusterKinds.AddMissingTokens); + normalizedChanges = changesWhenMissingTokens; + format = true; + } + var (newText, newSpans) = CopilotUtilities.GetNewTextAndChangedSpans(oldText, normalizedChanges); // Get the semantic model and keep it alive so none of the work we do causes it to be dropped. @@ -77,15 +104,19 @@ private static async Task> TryAdjustProposalInCurrent var (success, addImportChanges) = await TryGetAddImportTextChangesAsync( originalDocument, forkedDocument, normalizedChanges.First(), totalNewSpan, cancellationToken).ConfigureAwait(false); - if (!success) + if (success) + adjustmentKinds.Add(ProposalAdjusterKinds.AddMissingImports); + + if (adjustmentKinds.IsEmpty) return default; // Keep the new root around, in case something needs it while processing. This way we don't throw it away unnecessarily. GC.KeepAlive(forkedRoot); - // Reurn the add-import changes concatenated with the original changes. This way we ensure + // Return the add-import changes concatenated with the original changes. This way we ensure // that the copilot changes themselves are not themselves modified by the add-import changes. - return addImportChanges.Concat(normalizedChanges); + var totalChanges = addImportChanges.IsDefault ? normalizedChanges : addImportChanges.Concat(normalizedChanges); + return new(totalChanges, format, adjustmentKinds.ToImmutableAndClear()); } private static async Task<(bool success, ImmutableArray addImportChanges)> TryGetAddImportTextChangesAsync( diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs index bd1f7f9350b..3cf9965b222 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/CodeAnalysisDiagnosticAnalyzerService.cs @@ -16,103 +16,17 @@ namespace Microsoft.CodeAnalysis.Diagnostics; -internal static class CodeAnalysisDiagnosticAnalyzerServiceHelpers -{ - private static Func GetDiagnosticAnalyzerFilter( - Project project, DiagnosticAnalyzerInfoCache infoCache) - { - return analyzer => - { - if (analyzer == FileContentLoadAnalyzer.Instance || - analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance || - analyzer.IsCompilerAnalyzer()) - { - return true; - } - - if (analyzer.IsBuiltInAnalyzer()) - { - // always return true for builtin analyzer. we can't use - // descriptor check since many builtin analyzer always return - // hidden descriptor regardless what descriptor it actually - // return on runtime. they do this so that they can control - // severity through option page rather than rule set editor. - // this is special behavior only ide analyzer can do. we hope - // once we support editorconfig fully, third party can use this - // ability as well and we can remove this kind special treatment on builtin - // analyzer. - return true; - } - - if (analyzer is DiagnosticSuppressor) - { - // Always execute diagnostic suppressors. - return true; - } - - if (project.CompilationOptions is null) - { - // Skip compilation options based checks for non-C#/VB projects. - return true; - } - - // For most of analyzers, the number of diagnostic descriptors is small, so this should be cheap. - var descriptors = infoCache.GetDiagnosticDescriptors(analyzer); - var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); - return descriptors.Any(static (d, arg) => - { - var severity = d.GetEffectiveSeverity( - arg.CompilationOptions, - arg.analyzerConfigOptions?.ConfigOptionsWithFallback, - arg.analyzerConfigOptions?.TreeOptions); - return severity != ReportDiagnostic.Hidden; - }, - (project.CompilationOptions, analyzerConfigOptions)); - }; - } - - public static async Task> ForceCodeAnalysisDiagnosticsAsync( - IDiagnosticAnalyzerService diagnosticAnalyzerService, Project project, DiagnosticAnalyzerInfoCache infoCache, CancellationToken cancellationToken) - { - // We are being asked to explicitly analyze this project. As such we do *not* want to use the - // default rules determining which analyzers to run. For example, even if compiler diagnostics - // are set to 'none' for live diagnostics, we still want to run them here. - // - // As such, we are very intentionally not calling into _diagnosticAnalyzerService.GetDefaultAnalyzerFilter - // here. We want to control the rules entirely when this is called. - var filter = GetDiagnosticAnalyzerFilter(project, infoCache); - - // Compute all the diagnostics for all the documents in the project. - // - // Note: in this case we want diagnostics for source generated documents as well. So ensure those are - // generated and included in the results. - var sourceGeneratorDocuments = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); - - var documentDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project, [.. project.DocumentIds, .. project.AdditionalDocumentIds, .. sourceGeneratorDocuments.Select(d => d.Id)], - diagnosticIds: null, filter, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); - - // Then all the non-document diagnostics for that project as well. - var projectDiagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync( - project, diagnosticIds: null, filter, cancellationToken).ConfigureAwait(false); - - return [.. documentDiagnostics, .. projectDiagnostics]; - } -} - [ExportWorkspaceServiceFactory(typeof(ICodeAnalysisDiagnosticAnalyzerService)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class CodeAnalysisDiagnosticAnalyzerServiceFactory( - DiagnosticAnalyzerInfoCache.SharedGlobalCache infoCache) : IWorkspaceServiceFactory +internal sealed class CodeAnalysisDiagnosticAnalyzerServiceFactory() : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new CodeAnalysisDiagnosticAnalyzerService(infoCache.AnalyzerInfoCache, workspaceServices.Workspace); + => new CodeAnalysisDiagnosticAnalyzerService(workspaceServices.Workspace); private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagnosticAnalyzerService { private readonly IDiagnosticAnalyzerService _diagnosticAnalyzerService; - private readonly DiagnosticAnalyzerInfoCache _infoCache; private readonly Workspace _workspace; /// @@ -131,12 +45,9 @@ private sealed class CodeAnalysisDiagnosticAnalyzerService : ICodeAnalysisDiagno /// private readonly ConcurrentSet _clearedProjectIds = []; - public CodeAnalysisDiagnosticAnalyzerService( - DiagnosticAnalyzerInfoCache infoCache, - Workspace workspace) + public CodeAnalysisDiagnosticAnalyzerService(Workspace workspace) { _workspace = workspace; - _infoCache = infoCache; _diagnosticAnalyzerService = _workspace.Services.GetRequiredService(); _ = workspace.RegisterWorkspaceChangedHandler(OnWorkspaceChanged); @@ -178,8 +89,8 @@ public async ValueTask RunAnalysisAsync(Project project, CancellationToken cance Contract.ThrowIfFalse(project.Solution.Workspace == _workspace); - var diagnostics = await CodeAnalysisDiagnosticAnalyzerServiceHelpers.ForceCodeAnalysisDiagnosticsAsync( - _diagnosticAnalyzerService, project, _infoCache, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticAnalyzerService.ForceRunCodeAnalysisDiagnosticsAsync( + project, cancellationToken).ConfigureAwait(false); // Add the given project to the analyzed projects list **after** analysis has completed. // We need this ordering to ensure that 'HasProjectBeenAnalyzed' call above functions correctly. diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs index a00eb7c65a5..769656aaa43 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs @@ -5,9 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 580e6b6a701..295df004e97 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -24,25 +22,24 @@ internal interface IDiagnosticAnalyzerService : IWorkspaceService void RequestDiagnosticRefresh(); /// - /// The default analyzer filter that will be used in functions like if - /// no filter is provided. The default filter has the following rules: - /// - /// The standard compiler analyzer will not be run if the compiler diagnostic scope is . - /// A regular analyzer will not be run if is false. - /// A regular analyzer will not be run if if the background analysis scope is . - /// If a set of diagnostic ids are provided, the analyzer will not be run unless it declares at least one - /// descriptor in that set. - /// Otherwise, the analyzer will be run - /// + /// Forces analyzers to run on the given project and return all diagnostics, regardless of current environment + /// settings (like 'only run analyzers on open files', etc.). This is meant to be used by explicit invocations + /// of features like "Run Code Analysis". Note: not all analyzers will necessarily run. For example, analyzers + /// where all diagnostic descriptors are currently hidden will not run, as they would not produce any actual + /// diagnostics. /// - /// An additional filter that can accept or reject analyzers that the default - /// rules have accepted. - Func GetDefaultAnalyzerFilter( - Project project, ImmutableHashSet? diagnosticIds, Func? additionalFilter = null); + Task> ForceRunCodeAnalysisDiagnosticsAsync( + Project project, CancellationToken cancellationToken); - /// - Task> GetDeprioritizationCandidatesAsync( - Project project, ImmutableArray analyzers, CancellationToken cancellationToken); + /// + /// Returns if any of the given diagnostic IDs belong to an analyzer that is considered + /// 'deprioritized'. Deprioritized analyzers are ones that are considered expensive, due to registering symbol-end + /// and semantic-model actions. Because of their high cost, we want to avoid running them in the priority case, and only run them in the case. + /// + Task IsAnyDiagnosticIdDeprioritizedAsync( + Project project, ImmutableArray diagnosticIds, CancellationToken cancellationToken); /// /// Gets document diagnostics of the given diagnostic ids and/or analyzers from the given project. @@ -55,10 +52,7 @@ Task> GetDeprioritizationCandidatesAsync( /// Optional documents to scope the returned diagnostics. If , /// then diagnostics will be returned for and . /// Optional set of diagnostic IDs to scope the returned diagnostics. - /// Optional callback to filter out analyzers to execute for computing diagnostics. - /// If not present, will be used. If present, no default behavior - /// is used, and the callback is defered to entirely. To augment the existing default rules call - /// explicitly, and pass the result of that into this method. + /// Which analyzers to run. /// /// Indicates if local document diagnostics must be returned. /// Local diagnostics are the ones that are reported by analyzers on the same file for which the callback was received @@ -70,7 +64,7 @@ Task> GetDeprioritizationCandidatesAsync( /// project must be analyzed to get the complete set of non-local document diagnostics. /// Task> GetDiagnosticsForIdsAsync( - Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken); + Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids and/or analyzers from @@ -80,42 +74,39 @@ Task> GetDiagnosticsForIdsAsync( /// /// Project to fetch the diagnostics for. /// Optional set of diagnostic IDs to scope the returned diagnostics. - /// Optional callback to filter out analyzers to execute for computing diagnostics. - /// If not present, will be used. If present, no default behavior - /// is used, and the callback is defered to entirely. To augment the existing default rules call - /// explicitly, and pass the result of that into this method. + /// Which analyzers to run. Task> GetProjectDiagnosticsForIdsAsync( - Project project, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, CancellationToken cancellationToken); + Project project, ImmutableHashSet? diagnosticIds, AnalyzerFilter analyzerFilter, CancellationToken cancellationToken); /// /// Return up to date diagnostics for the given span for the document /// - /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. - /// Predicate filters out analyzers from execution if - /// none of its reported diagnostics should be included in the result. - /// /// Non-local diagnostics for the requested document are not returned. In other words, only the diagnostics /// produced by running the requested filtered set of analyzers only on this document are returned here. /// To get non-local diagnostics for a document, use . Non-local diagnostics /// will always be returned for the document in that case. /// Task> GetDiagnosticsForSpanAsync( - TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider priorityProvider, + TextDocument document, TextSpan? range, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, DiagnosticKind diagnosticKind, CancellationToken cancellationToken); + /// + Task>> GetDiagnosticDescriptorsPerReferenceAsync( + Solution solution, ProjectId? projectId, CancellationToken cancellationToken); + /// A project within where can be found Task> GetDiagnosticDescriptorsAsync( Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken); - /// - Task>> GetDiagnosticDescriptorsPerReferenceAsync( + /// + /// For all analyzers in the given solution, return the descriptor ids of all compilation end diagnostics. + /// Note: this does not include the "built in compiler analyzer". + /// + Task> GetCompilationEndDiagnosticDescriptorIdsAsync( Solution solution, CancellationToken cancellationToken); - - /// - Task>> GetDiagnosticDescriptorsPerReferenceAsync( - Project project, CancellationToken cancellationToken); } internal static class IDiagnosticAnalyzerServiceExtensions @@ -127,12 +118,12 @@ internal static class IDiagnosticAnalyzerServiceExtensions /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. /// /// - public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, - TextDocument document, TextSpan? range, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) + public static Task> GetDiagnosticsForSpanAsync( + this IDiagnosticAnalyzerService service, TextDocument document, TextSpan? range, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) => service.GetDiagnosticsForSpanAsync( document, range, diagnosticId: null, - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + priority: null, diagnosticKind, cancellationToken); @@ -146,12 +137,23 @@ public static Task> GetDiagnosticsForSpanAsync(th /// public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, TextDocument document, TextSpan? range, string? diagnosticId, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { - Func? shouldIncludeDiagnostic = diagnosticId != null ? id => id == diagnosticId : null; - return service.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic, - priorityProvider, diagnosticKind, cancellationToken); + var filter = diagnosticId != null + ? DiagnosticIdFilter.Include([diagnosticId]) + : DiagnosticIdFilter.All; + return service.GetDiagnosticsForSpanAsync( + document, range, filter, priority, diagnosticKind, cancellationToken); } + + public static Task>> GetDiagnosticDescriptorsPerReferenceAsync( + this IDiagnosticAnalyzerService service, Solution solution, CancellationToken cancellationToken) + => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, projectId: null, cancellationToken); + + public static Task>> GetDiagnosticDescriptorsPerReferenceAsync( + this IDiagnosticAnalyzerService service, Project project, CancellationToken cancellationToken) + => service.GetDiagnosticDescriptorsPerReferenceAsync(project.Solution, project.Id, cancellationToken); + } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs index f43204d5ab3..2ff08e3318d 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Options/DiagnosticOptionsStorage.cs @@ -10,7 +10,4 @@ internal sealed class DiagnosticOptionsStorage { public static readonly Option2 LogTelemetryForBackgroundAnalyzerExecution = new( "dotnet_log_telemetry_for_background_analyzer_execution", defaultValue: false); - - public static readonly Option2 LightbulbSkipExecutingDeprioritizedAnalyzers = new( - "dotnet_lightbulb_skip_executing_deprioritized_analyzers", defaultValue: false); } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs index 58aa721c7ba..0b94a695210 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.HostAnalyzerInfo.cs @@ -74,7 +74,7 @@ private static int GetPriority(DiagnosticAnalyzer state) /// /// Return s for the given . /// - public ImmutableArray GetProjectAnalyzers(Project project) + private ImmutableArray GetProjectAnalyzers_OnlyCallInProcess(Project project) { if (!s_projectToAnalyzers.TryGetValue(project, out var lazyAnalyzers)) { @@ -94,15 +94,15 @@ public ImmutableArray GetProjectAnalyzers(Project project) ImmutableArray ComputeProjectAnalyzers() { - var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); - var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo(project); + var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); + var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo_OnlyCallInProcess(project); return hostAnalyzerInfo.OrderedAllAnalyzers.AddRange(projectAnalyzerInfo.Analyzers); } } - private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo(Project project) + private HostAnalyzerInfo GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(Project project) { - var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo(project); + var projectAnalyzerInfo = GetOrCreateProjectAnalyzerInfo_OnlyCallInProcess(project); var solution = project.Solution; var key = new HostAnalyzerInfoKey(project.Language, project.State.HasSdkCodeStyleAnalyzers, solution.SolutionState.Analyzers.HostAnalyzerReferences); diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs index 3ed6b47e911..c3e86428b5d 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.ProjectStates.cs @@ -37,7 +37,7 @@ internal ProjectAnalyzerInfo( } } - private ProjectAnalyzerInfo GetOrCreateProjectAnalyzerInfo(Project project) + private ProjectAnalyzerInfo GetOrCreateProjectAnalyzerInfo_OnlyCallInProcess(Project project) { return ImmutableInterlocked.GetOrAdd( ref _projectAnalyzerStateMap, diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs index 7b667debb71..2749d28773c 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -25,7 +26,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed class DiagnosticAnalyzerServiceFactory( IGlobalOptionService globalOptions, IDiagnosticsRefresher diagnosticsRefresher, - DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, [Import(AllowDefault = true)] IAsynchronousOperationListenerProvider? listenerProvider) : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) @@ -33,7 +33,6 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) return new DiagnosticAnalyzerService( globalOptions, diagnosticsRefresher, - globalCache, listenerProvider, workspaceServices.Workspace); } @@ -57,7 +56,7 @@ internal sealed partial class DiagnosticAnalyzerService private readonly IGlobalOptionService _globalOptions; private readonly IDiagnosticsRefresher _diagnosticsRefresher; - private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache; + private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache = new(); private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); @@ -76,11 +75,9 @@ internal sealed partial class DiagnosticAnalyzerService public DiagnosticAnalyzerService( IGlobalOptionService globalOptions, IDiagnosticsRefresher diagnosticsRefresher, - DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, IAsynchronousOperationListenerProvider? listenerProvider, Workspace workspace) { - _analyzerInfoCache = globalCache.AnalyzerInfoCache; _listener = listenerProvider?.GetListener(FeatureAttribute.DiagnosticService) ?? AsynchronousOperationListenerProvider.NullListener; _globalOptions = globalOptions; _diagnosticsRefresher = diagnosticsRefresher; @@ -119,77 +116,46 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) public void RequestDiagnosticRefresh() => _diagnosticsRefresher.RequestWorkspaceRefresh(); - private ImmutableArray GetDiagnosticAnalyzers( - Project project, - ImmutableHashSet? diagnosticIds, - Func? shouldIncludeAnalyzer) + public TestAccessor GetTestAccessor() + => new(this); + + public readonly struct TestAccessor(DiagnosticAnalyzerService service) { - shouldIncludeAnalyzer ??= GetDefaultAnalyzerFilter(project, diagnosticIds, additionalFilter: null); + public ImmutableArray GetAnalyzers(Project project) + => service.GetProjectAnalyzers_OnlyCallInProcess(project); - var analyzersForProject = GetProjectAnalyzers(project); - return analyzersForProject.WhereAsArray(shouldIncludeAnalyzer); - } + public Task> AnalyzeProjectInProcessAsync( + Project project, CompilationWithAnalyzers compilationWithAnalyzers, bool logPerformanceInfo, bool getTelemetryInfo, CancellationToken cancellationToken) + => service.AnalyzeInProcessAsync(documentAnalysisScope: null, project, compilationWithAnalyzers, logPerformanceInfo, getTelemetryInfo, cancellationToken); - public Func GetDefaultAnalyzerFilter( - Project project, ImmutableHashSet? diagnosticIds, Func? additionalFilter) - => analyzer => + public async Task> GetDeprioritizedAnalyzersAsync(Project project) { - if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, this._globalOptions)) - return false; - - if (additionalFilter != null && !additionalFilter(analyzer)) - return false; + using var _ = ArrayBuilder.GetInstance(out var builder); - if (diagnosticIds != null && _analyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) - return false; + foreach (var analyzer in service.GetProjectAnalyzers_OnlyCallInProcess(project)) + { + if (await service.IsDeprioritizedAnalyzerAsync(project, analyzer, CancellationToken.None).ConfigureAwait(false)) + builder.Add(analyzer); + } - return true; - }; + return builder.ToImmutableAndClear(); + } - public Task> GetDiagnosticsForIdsAsync( - Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken) - { - var analyzers = GetDiagnosticAnalyzers(project, diagnosticIds, shouldIncludeAnalyzer); - - return ProduceProjectDiagnosticsAsync( - project, analyzers, diagnosticIds, - // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this - // project if no specific document id was requested. - documentIds.IsDefault ? [.. project.DocumentIds, .. project.AdditionalDocumentIds] : documentIds, - includeLocalDocumentDiagnostics, - includeNonLocalDocumentDiagnostics: true, - // return diagnostics specific to one project or document - includeProjectNonLocalResult: documentIds.IsDefault, - cancellationToken); - } + public async Task> GetDeprioritizedDiagnosticIdsAsync(Project project) + { + var builder = ImmutableHashSet.CreateBuilder(); - public Task> GetProjectDiagnosticsForIdsAsync( - Project project, - ImmutableHashSet? diagnosticIds, - Func? shouldIncludeAnalyzer, - CancellationToken cancellationToken) - { - var analyzers = GetDiagnosticAnalyzers(project, diagnosticIds, shouldIncludeAnalyzer); - - return ProduceProjectDiagnosticsAsync( - project, analyzers, diagnosticIds, - documentIds: [], - includeLocalDocumentDiagnostics: false, - includeNonLocalDocumentDiagnostics: false, - includeProjectNonLocalResult: true, - cancellationToken); - } + await service.PopulateDeprioritizedDiagnosticIdMapAsync(project, CancellationToken.None).ConfigureAwait(false); - public TestAccessor GetTestAccessor() - => new(this); + foreach (var analyzer in service.GetProjectAnalyzers_OnlyCallInProcess(project)) + { + Contract.ThrowIfFalse(DiagnosticAnalyzerService.s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var set)); + if (set != null) + builder.UnionWith(set); + } - public readonly struct TestAccessor(DiagnosticAnalyzerService service) - { - public ImmutableArray GetAnalyzers(Project project) - => service.GetProjectAnalyzers(project); + return builder.ToImmutableHashSet(); - public Task> AnalyzeProjectInProcessAsync( - Project project, CompilationWithAnalyzersPair compilationWithAnalyzers, bool logPerformanceInfo, bool getTelemetryInfo, CancellationToken cancellationToken) - => service.AnalyzeInProcessAsync(documentAnalysisScope: null, project, compilationWithAnalyzers, logPerformanceInfo, getTelemetryInfo, cancellationToken); + } } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs index 67710bdd738..2d8814d33c9 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CompilationWithAnalyzersPair.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.Runtime.CompilerServices; @@ -18,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed partial class DiagnosticAnalyzerService { /// - /// Cached data from a to the s + /// Cached data from a to the s /// we've created for it. Note: the CompilationWithAnalyzersPair instance is dependent on the set of s passed along with the project. /// @@ -33,14 +32,14 @@ private static readonly ConditionalWeakTable< ProjectState, SmallDictionary< (Checksum checksum, ImmutableArray analyzers), - AsyncLazy>> s_projectToCompilationWithAnalyzers = new(); + AsyncLazy>> s_projectToCompilationWithAnalyzers = new(); - /// + /// /// Protection around the SmallDictionary in . /// private static readonly SemaphoreSlim s_gate = new(initialCount: 1); - private static async Task GetOrCreateCompilationWithAnalyzersAsync( + private static async Task GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( Project project, ImmutableArray analyzers, HostAnalyzerInfo hostAnalyzerInfo, @@ -57,7 +56,7 @@ private static readonly ConditionalWeakTable< var map = s_projectToCompilationWithAnalyzers.GetValue( project.State, static _ => new(ChecksumAndAnalyzersEqualityComparer.Instance)); - AsyncLazy? lazy; + AsyncLazy? lazy; using (await s_gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { var checksumAndAnalyzers = (checksum, analyzers); @@ -75,7 +74,7 @@ private static readonly ConditionalWeakTable< // // Should only be called on a that . // - static async Task CreateCompilationWithAnalyzersAsync( + static async Task CreateCompilationWithAnalyzersAsync( (Project project, ImmutableArray analyzers, HostAnalyzerInfo hostAnalyzerInfo, @@ -84,62 +83,74 @@ private static readonly ConditionalWeakTable< { var (project, analyzers, hostAnalyzerInfo, crashOnAnalyzerException) = tuple; - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - - var projectAnalyzers = analyzers.WhereAsArray(static (s, info) => !info.IsHostAnalyzer(s), hostAnalyzerInfo); - var hostAnalyzers = analyzers.WhereAsArray(static (s, info) => info.IsHostAnalyzer(s), hostAnalyzerInfo); - - // Create driver that holds onto compilation and associated analyzers - var filteredProjectAnalyzers = projectAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); - var filteredHostAnalyzers = hostAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()); - var filteredProjectSuppressors = filteredProjectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor); - filteredHostAnalyzers = filteredHostAnalyzers.AddRange(filteredProjectSuppressors); + // Ensure we filter out DocumentDiagnosticAnalyzers (they're used to get diagnostics, without involving a + // compilation), and also ensure the list has no duplicates. + analyzers = analyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer()).Distinct(); // PERF: there is no analyzers for this compilation. // compilationWithAnalyzer will throw if it is created with no analyzers which is perf optimization. - if (filteredProjectAnalyzers.IsEmpty && filteredHostAnalyzers.IsEmpty) - { + if (analyzers.IsEmpty) return null; - } - - var exceptionFilter = (Exception ex) => - { - if (ex is not OperationCanceledException && crashOnAnalyzerException) - { - // report telemetry - FatalError.ReportAndPropagate(ex); - - // force fail fast (the host might not crash when reporting telemetry): - FailFast.OnFatalException(ex); - } - - return true; - }; - - // Create driver that holds onto compilation and associated analyzers - return new( - CreateCompilationWithAnalyzers(compilation, filteredProjectAnalyzers, project.State.ProjectAnalyzerOptions, exceptionFilter), - CreateCompilationWithAnalyzers(compilation, filteredHostAnalyzers, project.HostAnalyzerOptions, exceptionFilter)); - } - static CompilationWithAnalyzers? CreateCompilationWithAnalyzers( - Compilation compilation, - ImmutableArray analyzers, - AnalyzerOptions? options, - Func exceptionFilter) - { - if (analyzers.Length == 0) - return null; + var (sharedOptions, analyzerSpecificOptionsFactory) = GetOptions(); - return compilation.WithAnalyzers(analyzers, new CompilationWithAnalyzersOptions( - options: options, - onAnalyzerException: null, - analyzerExceptionFilter: exceptionFilter, - // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to - // async being used with synchronous blocking concurrency. - concurrentAnalysis: false, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true)); + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + return compilation.WithAnalyzers( + analyzers, + new CompilationWithAnalyzersOptions( + options: sharedOptions, + onAnalyzerException: null, + // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to + // async being used with synchronous blocking concurrency. + concurrentAnalysis: false, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: true, + getAnalyzerConfigOptionsProvider: analyzerSpecificOptionsFactory, + analyzerExceptionFilter: ex => + { + if (ex is not OperationCanceledException && crashOnAnalyzerException) + { + // report telemetry + FatalError.ReportAndPropagate(ex); + + // force fail fast (the host might not crash when reporting telemetry): + FailFast.OnFatalException(ex); + } + + return true; + })); + + (AnalyzerOptions sharedOptions, Func? analyzerSpecificOptionsFactory) GetOptions() + { + var projectAnalyzers = analyzers.Where(a => !hostAnalyzerInfo.IsHostAnalyzer(a)).ToSet(); + + // If we're all host analyzers and no project analyzers (which we can check if we just have 0 project + // analyzers), we can just return the options for host analyzers and not need any special logic. + // + // We want to do this (as opposed to passing back the lambda below in either of these cases) as + // the compiler optimizes this in src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs + // to effectively no-op this and only add the cost of a null-check (which will then should be + // optimized out by branch + if (projectAnalyzers.Count == 0) + return (project.State.HostAnalyzerOptions, null); + + // Similarly, If we're all project analyzers and no host analyzers, then just return the project + // analyzer specific options. + if (projectAnalyzers.Count == analyzers.Length) + return (project.State.ProjectAnalyzerOptions, null); + + // Ok, we have both host analyzers and project analyzers. in that case, we want to provide + // specific options for the project analyzers. Specifically, these options will be whatever + // is in EditorConfig for the project, *without* falling back to host options. That way + // they don't accidentally pick up options users set for their VS instance for other solutions. + // instead, they'll only get what is in editorconfig for the project, which is what the command + // line will do as well. + return ( + project.State.HostAnalyzerOptions, + analyzer => projectAnalyzers.Contains(analyzer) + ? project.State.ProjectAnalyzerOptions.AnalyzerConfigOptionsProvider + : project.State.HostAnalyzerOptions.AnalyzerConfigOptionsProvider); + } } } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ComputeDiagnosticAnalysisResults.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ComputeDiagnosticAnalysisResults.cs index a8a8256a4a4..c65f0e2ee17 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ComputeDiagnosticAnalysisResults.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ComputeDiagnosticAnalysisResults.cs @@ -23,7 +23,7 @@ internal sealed partial class DiagnosticAnalyzerService /// from cache or by calculating them. /// private async Task> ComputeDiagnosticAnalysisResultsInProcessAsync( - CompilationWithAnalyzersPair? compilationWithAnalyzers, + CompilationWithAnalyzers? compilationWithAnalyzers, Project project, ImmutableArray analyzers, CancellationToken cancellationToken) @@ -93,8 +93,7 @@ async Task> Co var result = ImmutableDictionary.Empty; // can be null if given project doesn't support compilation. - if (compilationWithAnalyzers?.ProjectAnalyzers.Length > 0 - || compilationWithAnalyzers?.HostAnalyzers.Length > 0) + if (compilationWithAnalyzers?.Analyzers.Length > 0) { // calculate regular diagnostic analyzers diagnostics var resultMap = await this.AnalyzeInProcessAsync( @@ -121,7 +120,7 @@ async Task> Me { try { - var compilation = compilationWithAnalyzers?.HostCompilation; + var compilation = compilationWithAnalyzers?.Compilation; foreach (var documentAnalyzer in ideAnalyzers) { diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CoreAnalyze.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CoreAnalyze.cs index 6fc1cbaf705..bd3629976a2 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CoreAnalyze.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_CoreAnalyze.cs @@ -3,16 +3,21 @@ // 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.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; +using Roslyn.Utilities; using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -22,7 +27,7 @@ internal sealed partial class DiagnosticAnalyzerService private async Task> AnalyzeInProcessAsync( DocumentAnalysisScope? documentAnalysisScope, Project project, - CompilationWithAnalyzersPair compilationWithAnalyzers, + CompilationWithAnalyzers compilationWithAnalyzers, bool logPerformanceInfo, bool getTelemetryInfo, CancellationToken cancellationToken) @@ -33,8 +38,8 @@ private async Task> AnalyzeAsync() { - var (analysisResult, additionalPragmaSuppressionDiagnostics) = await compilationWithAnalyzers.GetAnalysisResultAsync( - documentAnalysisScope, project, _analyzerInfoCache, cancellationToken).ConfigureAwait(false); + var analysisResult = await GetAnalysisResultAsync().ConfigureAwait(false); + var additionalPragmaSuppressionDiagnostics = await GetPragmaSuppressionAnalyzerDiagnosticsAsync().ConfigureAwait(false); if (logPerformanceInfo) { @@ -45,8 +50,7 @@ async Task.Empty; if (getTelemetryInfo && analysisResult is not null) { - telemetry = analysisResult.MergedAnalyzerTelemetryInfo; + telemetry = analysisResult.AnalyzerTelemetryInfo; } return DiagnosticAnalysisResultMap.Create(result, telemetry); } - void ReportAnalyzerPerformance(AnalysisResultPair? analysisResult) + void ReportAnalyzerPerformance(AnalysisResult analysisResult) { try { @@ -80,7 +84,7 @@ void ReportAnalyzerPerformance(AnalysisResultPair? analysisResult) ImmutableArray performanceInfo = []; if (analysisResult is not null) { - performanceInfo = performanceInfo.AddRange(analysisResult.MergedAnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(_analyzerInfoCache)); + performanceInfo = performanceInfo.AddRange(analysisResult.AnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(_analyzerInfoCache)); } using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_ReportAnalyzerPerformance, cancellationToken)) @@ -94,5 +98,106 @@ void ReportAnalyzerPerformance(AnalysisResultPair? analysisResult) // ignore all, this is fire and forget method } } + + async Task GetAnalysisResultAsync() + { + if (documentAnalysisScope == null) + { + return await compilationWithAnalyzers.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); + } + + Debug.Assert(documentAnalysisScope.Analyzers.ToSet().IsSubsetOf(compilationWithAnalyzers.Analyzers)); + + switch (documentAnalysisScope.Kind) + { + case AnalysisKind.Syntax: + if (documentAnalysisScope.TextDocument is Document document) + { + var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + return await compilationWithAnalyzers.GetAnalysisResultAsync(tree, documentAnalysisScope.Span, documentAnalysisScope.Analyzers, cancellationToken).ConfigureAwait(false); + } + else + { + return await compilationWithAnalyzers.GetAnalysisResultAsync(documentAnalysisScope.AdditionalFile, documentAnalysisScope.Span, documentAnalysisScope.Analyzers, cancellationToken).ConfigureAwait(false); + } + + case AnalysisKind.Semantic: + var model = await ((Document)documentAnalysisScope.TextDocument).GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + return await compilationWithAnalyzers.GetAnalysisResultAsync(model, documentAnalysisScope.Span, documentAnalysisScope.Analyzers, cancellationToken).ConfigureAwait(false); + + default: + throw ExceptionUtilities.UnexpectedValue(documentAnalysisScope.Kind); + } + } + + async Task> GetPragmaSuppressionAnalyzerDiagnosticsAsync() + { + var hostAnalyzerInfo = this.GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); + var analyzers = documentAnalysisScope?.Analyzers ?? compilationWithAnalyzers.Analyzers; + + // NOTE: It is unclear why we filter down to host analyzers here, instead of just using all the specified + // analyzers. This behavior is historical, and we are preserving it for now. However, it may be incorrect + // and could be changed in the future if we find a scenario that requires it. + var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.IsHostAnalyzer(a), hostAnalyzerInfo); + + var suppressionAnalyzer = hostAnalyzers.OfType().FirstOrDefault(); + if (suppressionAnalyzer == null) + return []; + + if (documentAnalysisScope != null) + { + if (documentAnalysisScope.TextDocument is not Document document) + return []; + + using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); + await AnalyzeDocumentAsync( + compilationWithAnalyzers, _analyzerInfoCache, suppressionAnalyzer, + document, documentAnalysisScope.Span, diagnosticsBuilder.Add, cancellationToken).ConfigureAwait(false); + return diagnosticsBuilder.ToImmutableAndClear(); + } + else + { + if (compilationWithAnalyzers.AnalysisOptions.ConcurrentAnalysis) + { + return await ProducerConsumer.RunParallelAsync( + source: project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken), + produceItems: static async (document, callback, args, cancellationToken) => + { + var (compilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer) = args; + await AnalyzeDocumentAsync( + compilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer, + document, span: null, callback, cancellationToken).ConfigureAwait(false); + }, + args: (compilationWithAnalyzers, _analyzerInfoCache, suppressionAnalyzer), + cancellationToken).ConfigureAwait(false); + } + else + { + using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); + await foreach (var document in project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) + { + await AnalyzeDocumentAsync( + compilationWithAnalyzers, _analyzerInfoCache, suppressionAnalyzer, + document, span: null, diagnosticsBuilder.Add, cancellationToken).ConfigureAwait(false); + } + + return diagnosticsBuilder.ToImmutableAndClear(); + } + } + } + + static async Task AnalyzeDocumentAsync( + CompilationWithAnalyzers hostCompilationWithAnalyzers, + DiagnosticAnalyzerInfoCache analyzerInfoCache, + IPragmaSuppressionsAnalyzer suppressionAnalyzer, + Document document, + TextSpan? span, + Action reportDiagnostic, + CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + await suppressionAnalyzer.AnalyzeAsync( + semanticModel, span, hostCompilationWithAnalyzers, analyzerInfoCache.GetDiagnosticDescriptors, reportDiagnostic, cancellationToken).ConfigureAwait(false); + } } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs index 3327c42192a..83a7cd9a11d 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_DeprioritizationCandidates.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 System; using System.Collections.Immutable; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -21,46 +23,67 @@ internal sealed partial class DiagnosticAnalyzerService /// We accept that this cache may be inaccurate in such scenarios as they are likely rare, and this only /// serves as a simple heuristic to order analyzer execution. If wrong, it's not a major deal. /// - private static readonly ConditionalWeakTable> s_analyzerToIsDeprioritizationCandidateMap = new(); + private static readonly ConditionalWeakTable?> s_analyzerToDeprioritizedDiagnosticIds = new(); - private async Task> GetDeprioritizationCandidatesInProcessAsync( - Project project, ImmutableArray analyzers, CancellationToken cancellationToken) + private async Task IsDeprioritizedAnalyzerAsync( + Project project, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var builder); + await PopulateDeprioritizedDiagnosticIdMapAsync(project, cancellationToken).ConfigureAwait(false); - HostAnalyzerInfo? hostAnalyzerInfo = null; - CompilationWithAnalyzersPair? compilationWithAnalyzers = null; + // this can't fail as the above call populates the CWT entries for all analyzers within that project if missing. + Contract.ThrowIfFalse(s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var set)); + return set != null; + } + + private async ValueTask PopulateDeprioritizedDiagnosticIdMapAsync(Project project, CancellationToken cancellationToken) + { + await IsAnyDiagnosticIdDeprioritizedAsync(project, diagnosticIds: [], cancellationToken).ConfigureAwait(false); + } + + public async Task IsAnyDeprioritizedDiagnosticIdInProcessAsync( + Project project, ImmutableArray diagnosticIds, CancellationToken cancellationToken) + { + CompilationWithAnalyzers? compilationWithAnalyzers = null; + + var analyzers = GetProjectAnalyzers_OnlyCallInProcess(project); foreach (var analyzer in analyzers) { - if (!s_analyzerToIsDeprioritizationCandidateMap.TryGetValue(analyzer, out var boxedBool)) + if (!s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var deprioritizedIds)) { - if (hostAnalyzerInfo is null) + if (compilationWithAnalyzers is null) { - hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); - compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( - project, analyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); + compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( + project, analyzers, GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project), this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); } - boxedBool = new(await IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(analyzer).ConfigureAwait(false)); + deprioritizedIds = await ComputeDeprioritizedDiagnosticIdsAsync(analyzer).ConfigureAwait(false); + #if NET - s_analyzerToIsDeprioritizationCandidateMap.TryAdd(analyzer, boxedBool); + s_analyzerToDeprioritizedDiagnosticIds.TryAdd(analyzer, deprioritizedIds); #else - lock (s_analyzerToIsDeprioritizationCandidateMap) + lock (s_analyzerToDeprioritizedDiagnosticIds) { - if (!s_analyzerToIsDeprioritizationCandidateMap.TryGetValue(analyzer, out var existing)) - s_analyzerToIsDeprioritizationCandidateMap.Add(analyzer, boxedBool); + if (!s_analyzerToDeprioritizedDiagnosticIds.TryGetValue(analyzer, out var existing)) + s_analyzerToDeprioritizedDiagnosticIds.Add(analyzer, deprioritizedIds); } #endif + } - if (boxedBool.Value) - builder.Add(analyzer); + if (deprioritizedIds != null) + { + foreach (var id in diagnosticIds) + { + if (deprioritizedIds.Contains(id)) + return true; + } + } } - return builder.ToImmutableAndClear(); + return false; - async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(DiagnosticAnalyzer analyzer) + async ValueTask?> ComputeDeprioritizedDiagnosticIdsAsync(DiagnosticAnalyzer analyzer) { // We deprioritize SymbolStart/End and SemanticModel analyzers from 'Normal' to 'Low' priority bucket, // as these are computationally more expensive. @@ -69,14 +92,17 @@ async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(Dia analyzer.IsWorkspaceDiagnosticAnalyzer() || analyzer.IsCompilerAnalyzer()) { - return false; + return null; } var telemetryInfo = await compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false); if (telemetryInfo == null) - return false; + return null; + + if (telemetryInfo.SymbolStartActionsCount == 0 && telemetryInfo.SemanticModelActionsCount == 0) + return null; - return telemetryInfo.SymbolStartActionsCount > 0 || telemetryInfo.SemanticModelActionsCount > 0; + return [.. analyzer.SupportedDiagnostics.Select(d => d.Id)]; } } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.cs new file mode 100644 index 00000000000..1f42875aea4 --- /dev/null +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ForceCodeAnalysisDiagnostics.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. + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +internal sealed partial class DiagnosticAnalyzerService +{ + public async ValueTask> ForceRunCodeAnalysisDiagnosticsInProcessAsync( + Project project, CancellationToken cancellationToken) + { + // We are being asked to explicitly analyze this project. As such we do *not* want to use the + // default rules determining which analyzers to run. For example, even if compiler diagnostics + // are set to 'none' for live diagnostics, we still want to run them here. + // + // As such, we are very intentionally not calling into this.GetDefaultAnalyzerFilter + // here. We want to control the rules entirely when this is called. + var analyzers = GetProjectAnalyzers_OnlyCallInProcess(project); + var filteredAnalyzers = analyzers.WhereAsArray(ShouldIncludeAnalyzer); + + // Compute document and project diagnostics in parallel. + + // Compute all the diagnostics for all the documents in the project. + var documentDiagnosticsTask = GetDiagnosticsForIdsAsync(); + + // Then all the non-document diagnostics for that project as well. + var projectDiagnosticsTask = this.GetProjectDiagnosticsForIdsInProcessAsync( + project, diagnosticIds: null, filteredAnalyzers, cancellationToken); + + await Task.WhenAll(documentDiagnosticsTask, projectDiagnosticsTask).ConfigureAwait(false); + + return [.. await documentDiagnosticsTask.ConfigureAwait(false), .. await projectDiagnosticsTask.ConfigureAwait(false)]; + + async Task> GetDiagnosticsForIdsAsync() + { + + // Note: in this case we want diagnostics for source generated documents as well. So ensure those are + // generated and included in the results. + var sourceGeneratorDocuments = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); + + return await this.GetDiagnosticsForIdsInProcessAsync( + project, [.. project.DocumentIds, .. project.AdditionalDocumentIds, .. sourceGeneratorDocuments.Select(d => d.Id)], + diagnosticIds: null, filteredAnalyzers, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + } + + bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) + { + if (analyzer == FileContentLoadAnalyzer.Instance || + analyzer == GeneratorDiagnosticsPlaceholderAnalyzer.Instance || + analyzer.IsCompilerAnalyzer()) + { + return true; + } + + if (analyzer.IsBuiltInAnalyzer()) + { + // always return true for builtin analyzer. we can't use + // descriptor check since many builtin analyzer always return + // hidden descriptor regardless what descriptor it actually + // return on runtime. they do this so that they can control + // severity through option page rather than rule set editor. + // this is special behavior only ide analyzer can do. we hope + // once we support editorconfig fully, third party can use this + // ability as well and we can remove this kind special treatment on builtin + // analyzer. + return true; + } + + if (analyzer is DiagnosticSuppressor) + { + // Always execute diagnostic suppressors. + return true; + } + + if (project.CompilationOptions is null) + { + // Skip compilation options based checks for non-C#/VB projects. + return true; + } + + // For most of analyzers, the number of diagnostic descriptors is small, so this should be cheap. + var descriptors = this._analyzerInfoCache.GetDiagnosticDescriptors(analyzer); + var analyzerConfigOptions = project.GetAnalyzerConfigOptions(); + return descriptors.Any(static (d, arg) => + { + var severity = d.GetEffectiveSeverity( + arg.CompilationOptions, + arg.analyzerConfigOptions?.ConfigOptionsWithFallback, + arg.analyzerConfigOptions?.TreeOptions); + return severity != ReportDiagnostic.Hidden; + }, + (project.CompilationOptions, analyzerConfigOptions)); + } + } +} diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs index f7e96ec7181..5929a919f7f 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_GetDiagnosticsForSpan.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -28,7 +27,7 @@ private static async Task>.GetInstance(out var builder); - foreach (var analyzer in executor.AnalysisScope.ProjectAnalyzers.ConcatFast(executor.AnalysisScope.HostAnalyzers)) + foreach (var analyzer in executor.AnalysisScope.Analyzers) { var diagnostics = await executor.ComputeDiagnosticsInProcessAsync(analyzer, cancellationToken).ConfigureAwait(false); builder.Add(analyzer, diagnostics); @@ -37,26 +36,18 @@ private static async Task> GetDiagnosticsForSpanAsync( + public async Task> GetDiagnosticsForSpanInProcessAsync( TextDocument document, TextSpan? range, - Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider? priorityProvider, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { - // Note: due to the in-memory work that priorityProvider and shouldIncludeDiagnostic need to do, - // much of this function runs locally (in process) to determine which analyzers to run, before - // finally making a call out to OOP to actually do the work. - - // always make sure that analyzer is called on background thread. - await Task.Yield().ConfigureAwait(false); - priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var project = document.Project; - var unfilteredAnalyzers = GetProjectAnalyzers(project); + var unfilteredAnalyzers = GetProjectAnalyzers_OnlyCallInProcess(project); var analyzers = unfilteredAnalyzers .WhereAsArray(a => DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, project, _globalOptions)); @@ -72,25 +63,21 @@ public async Task> GetDiagnosticsForSpanAsync( // member edit analysis. This analysis is currently only enabled with LSP pull diagnostics. var incrementalAnalysis = range is null && document is Document { SupportsSyntaxTree: true }; - using var _1 = PooledHashSet.GetInstance(out var deprioritizationCandidates); - - deprioritizationCandidates.AddRange(await this.GetDeprioritizationCandidatesAsync( - project, analyzers, cancellationToken).ConfigureAwait(false)); - - var (syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers) = GetAllAnalyzers(); - syntaxAnalyzers = FilterAnalyzers(syntaxAnalyzers, AnalysisKind.Syntax, range, deprioritizationCandidates); - semanticSpanAnalyzers = FilterAnalyzers(semanticSpanAnalyzers, AnalysisKind.Semantic, range, deprioritizationCandidates); - semanticDocumentAnalyzers = FilterAnalyzers(semanticDocumentAnalyzers, AnalysisKind.Semantic, span: null, deprioritizationCandidates); + var (syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers) = await GetAllAnalyzersAsync().ConfigureAwait(false); + syntaxAnalyzers = await FilterAnalyzersAsync(syntaxAnalyzers, AnalysisKind.Syntax, range).ConfigureAwait(false); + semanticSpanAnalyzers = await FilterAnalyzersAsync(semanticSpanAnalyzers, AnalysisKind.Semantic, range).ConfigureAwait(false); + semanticDocumentAnalyzers = await FilterAnalyzersAsync(semanticDocumentAnalyzers, AnalysisKind.Semantic, span: null).ConfigureAwait(false); - var allDiagnostics = await this.ComputeDiagnosticsAsync( + var allDiagnostics = await this.ComputeDiagnosticsInProcessAsync( document, range, analyzers, syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers, incrementalAnalysis, logPerformanceInfo, cancellationToken).ConfigureAwait(false); return allDiagnostics.WhereAsArray(ShouldInclude); - (ImmutableArray syntaxAnalyzers, - ImmutableArray semanticSpanAnalyzers, - ImmutableArray semanticDocumentAnalyzers) GetAllAnalyzers() + async ValueTask<( + ImmutableArray syntaxAnalyzers, + ImmutableArray semanticSpanAnalyzers, + ImmutableArray semanticDocumentAnalyzers)> GetAllAnalyzersAsync() { try { @@ -104,11 +91,11 @@ public async Task> GetDiagnosticsForSpanAsync( using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); - using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priorityProvider.Priority.GetPriorityInt()}"); + using var _4 = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"Pri{priority.GetPriorityInt()}"); foreach (var analyzer in analyzers) { - if (!ShouldIncludeAnalyzer(analyzer, shouldIncludeDiagnostic, priorityProvider, this)) + if (!await ShouldIncludeAnalyzerAsync(analyzer).ConfigureAwait(false)) continue; bool includeSyntax = true, includeSemantic = true; @@ -166,15 +153,10 @@ public async Task> GetDiagnosticsForSpanAsync( } } - // Local functions - static bool ShouldIncludeAnalyzer( - DiagnosticAnalyzer analyzer, - Func? shouldIncludeDiagnostic, - ICodeActionRequestPriorityProvider priorityProvider, - DiagnosticAnalyzerService owner) + async ValueTask ShouldIncludeAnalyzerAsync(DiagnosticAnalyzer analyzer) { // Skip executing analyzer if its priority does not match the request priority. - if (!priorityProvider.MatchesPriority(analyzer)) + if (!await MatchesPriorityAsync(analyzer).ConfigureAwait(false)) return false; // Special case DocumentDiagnosticAnalyzer to never skip these document analyzers based on @@ -185,31 +167,69 @@ static bool ShouldIncludeAnalyzer( return true; // Skip analyzer if none of its reported diagnostics should be included. - if (shouldIncludeDiagnostic != null && - !owner._analyzerInfoCache.GetDiagnosticDescriptors(analyzer).Any(static (a, shouldIncludeDiagnostic) => shouldIncludeDiagnostic(a.Id), shouldIncludeDiagnostic)) + if (diagnosticIdFilter != DiagnosticIdFilter.All) { - return false; + var descriptors = _analyzerInfoCache.GetDiagnosticDescriptors(analyzer); + return diagnosticIdFilter.Allow(descriptors.Select(d => d.Id)); } return true; } - ImmutableArray FilterAnalyzers( + // + // Returns true if the given can report diagnostics that can have fixes from a code + // fix provider with matching . This method is useful for performing a performance + // optimization for lightbulb diagnostic computation, wherein we can reduce the set of analyzers to be executed + // when computing fixes for a specific . + // + async Task MatchesPriorityAsync(DiagnosticAnalyzer analyzer) + { + // If caller isn't asking for prioritized result, then run all analyzers. + if (priority is null) + return true; + + // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, + // which requires all analyzer diagnostics. + if (priority == CodeActionRequestPriority.Lowest) + return true; + + // The compiler analyzer always counts for any priority. It's diagnostics may be fixed + // by high pri or normal pri fixers. + if (analyzer.IsCompilerAnalyzer()) + return true; + + // Check if we are computing diagnostics for 'CodeActionRequestPriority.Low' and + // this analyzer was de-prioritized to low priority bucket. + if (priority == CodeActionRequestPriority.Low && + await this.IsDeprioritizedAnalyzerAsync(project, analyzer, cancellationToken).ConfigureAwait(false)) + { + return true; + } + + // Now compute this analyzer's priority and compare it with the provider's request 'Priority'. + // Our internal 'IBuiltInAnalyzer' can specify custom request priority, while all + // the third-party analyzers are assigned 'Medium' priority. + var analyzerPriority = analyzer is IBuiltInAnalyzer { IsHighPriority: true } + ? CodeActionRequestPriority.High + : CodeActionRequestPriority.Default; + + return priority == analyzerPriority; + } + + async Task> FilterAnalyzersAsync( ImmutableArray analyzers, AnalysisKind kind, - TextSpan? span, - HashSet deprioritizationCandidates) + TextSpan? span) { using var _1 = ArrayBuilder.GetInstance(analyzers.Length, out var filteredAnalyzers); foreach (var analyzer in analyzers) { - Debug.Assert(priorityProvider.MatchesPriority(analyzer)); - // Check if this is an expensive analyzer that needs to be de-prioritized to a lower priority bucket. // If so, we skip this analyzer from execution in the current priority bucket. // We will subsequently execute this analyzer in the lower priority bucket. - if (TryDeprioritizeAnalyzer(analyzer, kind, span, deprioritizationCandidates)) + if (await ShouldDeprioritizeAnalyzerAsync(analyzer, kind, span).ConfigureAwait(false)) continue; filteredAnalyzers.Add(analyzer); @@ -218,9 +238,8 @@ ImmutableArray FilterAnalyzers( return filteredAnalyzers.ToImmutableAndClear(); } - bool TryDeprioritizeAnalyzer( - DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? span, - HashSet deprioritizationCandidates) + async ValueTask ShouldDeprioritizeAnalyzerAsync( + DiagnosticAnalyzer analyzer, AnalysisKind kind, TextSpan? span) { // PERF: In order to improve lightbulb performance, we perform de-prioritization optimization for certain analyzers // that moves the analyzer to a lower priority bucket. However, to ensure that de-prioritization happens for very rare cases, @@ -229,44 +248,33 @@ bool TryDeprioritizeAnalyzer( // 2. We are processing 'CodeActionRequestPriority.Normal' priority request. // 3. Analyzer registers certain actions that are known to lead to high performance impact due to its broad analysis scope, // such as SymbolStart/End actions and SemanticModel actions. - // 4. Analyzer did not report a diagnostic on the same line in prior document snapshot. // Conditions 1. and 2. if (kind != AnalysisKind.Semantic || !span.HasValue || - priorityProvider.Priority != CodeActionRequestPriority.Default) + priority != CodeActionRequestPriority.Default) { return false; } - Debug.Assert(span.Value.Length < text.Length); - // Condition 3. // Check if this is a candidate analyzer that can be de-prioritized into a lower priority bucket based on registered actions. - if (!deprioritizationCandidates.Contains(analyzer)) - return false; - - // 'LightbulbSkipExecutingDeprioritizedAnalyzers' option determines if we want to execute this analyzer - // in low priority bucket or skip it completely. If the option is not set, track the de-prioritized - // analyzer to be executed in low priority bucket. - // Note that 'AddDeprioritizedAnalyzerWithLowPriority' call below mutates the state in the provider to - // track this analyzer. This ensures that when the owner of this provider calls us back to execute - // the low priority bucket, we can still get back to this analyzer and execute it that time. - if (!this._globalOptions.GetOption(DiagnosticOptionsStorage.LightbulbSkipExecutingDeprioritizedAnalyzers)) - priorityProvider.AddDeprioritizedAnalyzerWithLowPriority(analyzer); - - return true; + return await this.IsDeprioritizedAnalyzerAsync(project, analyzer, cancellationToken).ConfigureAwait(false); } bool ShouldInclude(DiagnosticData diagnostic) { - return diagnostic.DocumentId == document.Id && - (range == null || range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))) - && (shouldIncludeDiagnostic == null || shouldIncludeDiagnostic(diagnostic.Id)); + if (diagnostic.DocumentId != document.Id) + return false; + + if (range != null && !range.Value.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))) + return false; + + return diagnosticIdFilter.Allow(diagnostic.Id); } } - public async Task> ComputeDiagnosticsInProcessAsync( + private async Task> ComputeDiagnosticsInProcessAsync( TextDocument document, TextSpan? range, ImmutableArray allAnalyzers, @@ -280,8 +288,8 @@ public async Task> ComputeDiagnosticsInProcessAsy // We log performance info when we are computing diagnostics for a span var project = document.Project; - var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); - var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync( + var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); + var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( document.Project, allAnalyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); using var _ = ArrayBuilder.GetInstance(out var list); @@ -304,9 +312,7 @@ async Task ComputeDocumentDiagnosticsAsync( Debug.Assert(!incrementalAnalysis || kind == AnalysisKind.Semantic); Debug.Assert(!incrementalAnalysis || analyzers.All(analyzer => analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); - var projectAnalyzers = analyzers.WhereAsArray(static (a, info) => !info.IsHostAnalyzer(a), hostAnalyzerInfo); - var hostAnalyzers = analyzers.WhereAsArray(static (a, info) => info.IsHostAnalyzer(a), hostAnalyzerInfo); - var analysisScope = new DocumentAnalysisScope(document, span, projectAnalyzers, hostAnalyzers, kind); + var analysisScope = new DocumentAnalysisScope(document, span, analyzers, kind); var executor = new DocumentAnalysisExecutor(this, analysisScope, compilationWithAnalyzers, logPerformanceInfo); var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs index 267a4678e6e..0aec147ab79 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs @@ -16,11 +16,107 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed partial class DiagnosticAnalyzerService { - public async Task> ProduceProjectDiagnosticsInProcessAsync( + /// + /// Should only be called from other "InProcess" methods as this loads and realizes the DiagnosticAnalyzers. + /// + private ImmutableArray GetDiagnosticAnalyzersInProcess( + Project project, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter) + { + var analyzersForProject = GetProjectAnalyzers_OnlyCallInProcess(project); + return analyzersForProject.WhereAsArray(ShouldIncludeAnalyzer); + + bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) + { + if (analyzer.IsCompilerAnalyzer()) + { + if ((analyzerFilter & AnalyzerFilter.CompilerAnalyzer) == 0) + return false; + } + else + { + if ((analyzerFilter & AnalyzerFilter.NonCompilerAnalyzer) == 0) + return false; + } + + if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, this._globalOptions)) + return false; + + if (diagnosticIds != null && _analyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id))) + return false; + + return true; + } + } + + private Task> GetDiagnosticsForIdsInProcessAsync( Project project, + ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, + bool includeLocalDocumentDiagnostics, + CancellationToken cancellationToken) + { + return GetDiagnosticsForIdsInProcessAsync( + project, documentIds, diagnosticIds, + GetDiagnosticAnalyzersInProcess(project, diagnosticIds, analyzerFilter), + includeLocalDocumentDiagnostics, cancellationToken); + } + + private Task> GetDiagnosticsForIdsInProcessAsync( + Project project, + ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, ImmutableArray analyzers, + bool includeLocalDocumentDiagnostics, + CancellationToken cancellationToken) + { + return ProduceProjectDiagnosticsInProcessAsync( + project, diagnosticIds, + // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this + // project if no specific document id was requested. + documentIds.IsDefault ? [.. project.DocumentIds, .. project.AdditionalDocumentIds] : documentIds, + analyzers, + includeLocalDocumentDiagnostics, + includeNonLocalDocumentDiagnostics: true, + // return diagnostics specific to one project or document + includeProjectNonLocalResult: documentIds.IsDefault, + cancellationToken); + } + + private Task> GetProjectDiagnosticsForIdsInProcessAsync( + Project project, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, + CancellationToken cancellationToken) + { + return GetProjectDiagnosticsForIdsInProcessAsync( + project, diagnosticIds, + GetDiagnosticAnalyzersInProcess(project, diagnosticIds, analyzerFilter), + cancellationToken); + } + + private Task> GetProjectDiagnosticsForIdsInProcessAsync( + Project project, + ImmutableHashSet? diagnosticIds, + ImmutableArray analyzers, + CancellationToken cancellationToken) + { + return ProduceProjectDiagnosticsInProcessAsync( + project, diagnosticIds, documentIds: [], + analyzers, + includeLocalDocumentDiagnostics: false, + includeNonLocalDocumentDiagnostics: false, + includeProjectNonLocalResult: true, + cancellationToken); + } + + private async Task> ProduceProjectDiagnosticsInProcessAsync( + Project project, ImmutableHashSet? diagnosticIds, ImmutableArray documentIds, + ImmutableArray analyzers, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, bool includeProjectNonLocalResult, @@ -28,7 +124,7 @@ public async Task> ProduceProjectDiagnosticsInPro { using var _ = ArrayBuilder.GetInstance(out var builder); - var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo(project); + var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project); var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false); foreach (var analyzer in analyzers) @@ -71,7 +167,7 @@ async Task> Ge ImmutableArray analyzers) { // Otherwise, just compute for the analyzers we care about. - var compilation = await GetOrCreateCompilationWithAnalyzersAsync( + var compilation = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync( project, analyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false); var result = await ComputeDiagnosticAnalysisResultsInProcessAsync( diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs index f3a5c56fe8d..19393e6c24c 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService_RemoteOrLocalDispatcher.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; @@ -19,6 +20,24 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal sealed partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService { + public async Task> ForceRunCodeAnalysisDiagnosticsAsync( + Project project, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client is not null) + { + var descriptors = await client.TryInvokeAsync>( + project, + (service, solution, cancellationToken) => service.ForceRunCodeAnalysisDiagnosticsAsync( + solution, project.Id, cancellationToken), + cancellationToken).ConfigureAwait(false); + return descriptors.HasValue ? descriptors.Value : []; + } + + // Otherwise, fallback to computing in proc. + return await ForceRunCodeAnalysisDiagnosticsInProcessAsync(project, cancellationToken).ConfigureAwait(false); + } + public async Task> GetDiagnosticDescriptorsAsync( Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken) { @@ -43,34 +62,56 @@ public async Task> GetDiagnosticDescriptors .SelectManyAsArray(this._analyzerInfoCache.GetDiagnosticDescriptors); } - public async Task>> GetDiagnosticDescriptorsPerReferenceAsync(Solution solution, CancellationToken cancellationToken) + public async Task> GetCompilationEndDiagnosticDescriptorIdsAsync( + Solution solution, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); if (client is not null) { - var map = await client.TryInvokeAsync>>( + var result = await client.TryInvokeAsync>( solution, - (service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, cancellationToken), + (service, solution, cancellationToken) => service.GetCompilationEndDiagnosticDescriptorIdsAsync( + solution, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!map.HasValue) - return ImmutableDictionary>.Empty; - return map.Value.ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor())); + return result.HasValue ? result.Value : []; } - return solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache); + using var _1 = PooledHashSet.GetInstance(out var builder); + using var _2 = PooledHashSet<(object Reference, string Language)>.GetInstance(out var seenAnalyzerReferencesByLanguage); + + foreach (var project in solution.Projects) + { + var analyzersPerReferenceMap = solution.SolutionState.Analyzers.CreateDiagnosticAnalyzersPerReference(project); + foreach (var (analyzerReference, analyzers) in analyzersPerReferenceMap) + { + if (!seenAnalyzerReferencesByLanguage.Add((analyzerReference, project.Language))) + continue; + + foreach (var analyzer in analyzers) + { + if (analyzer.IsCompilerAnalyzer()) + continue; + + foreach (var buildOnlyDescriptor in _analyzerInfoCache.GetCompilationEndDiagnosticDescriptors(analyzer)) + builder.Add(buildOnlyDescriptor.Id); + } + } + } + + return builder.ToImmutableArray(); } - public async Task>> GetDiagnosticDescriptorsPerReferenceAsync(Project project, CancellationToken cancellationToken) + public async Task>> GetDiagnosticDescriptorsPerReferenceAsync( + Solution solution, ProjectId? projectId, CancellationToken cancellationToken) { - var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); if (client is not null) { var map = await client.TryInvokeAsync>>( - project, - (service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, project.Id, cancellationToken), + solution, + (service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync( + solution, projectId, cancellationToken), cancellationToken).ConfigureAwait(false); if (!map.HasValue) return ImmutableDictionary>.Empty; @@ -80,101 +121,90 @@ public async Task kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor())); } - return project.Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache, project); + return solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference( + this._analyzerInfoCache, solution.GetProject(projectId)); } - public async Task> GetDeprioritizationCandidatesAsync( - Project project, ImmutableArray analyzers, CancellationToken cancellationToken) + public async Task> GetDiagnosticsForIdsAsync( + Project project, ImmutableArray documentIds, ImmutableHashSet? diagnosticIds, AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var analyzerIds = analyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var result = await client.TryInvokeAsync>( + var result = await client.TryInvokeAsync>( project, - (service, solution, cancellationToken) => service.GetDeprioritizationCandidatesAsync( - solution, project.Id, analyzerIds, cancellationToken), + (service, solution, cancellationToken) => service.GetDiagnosticsForIdsAsync( + solution, project.Id, documentIds, diagnosticIds, analyzerFilter, includeLocalDocumentDiagnostics, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!result.HasValue) - return []; - - return analyzers.FilterAnalyzers(result.Value); + return result.HasValue ? result.Value : []; } - return await GetDeprioritizationCandidatesInProcessAsync(project, analyzers, cancellationToken).ConfigureAwait(false); + return await GetDiagnosticsForIdsInProcessAsync( + project, documentIds, diagnosticIds, + analyzerFilter, + includeLocalDocumentDiagnostics, + cancellationToken).ConfigureAwait(false); } - internal async Task> ProduceProjectDiagnosticsAsync( + public async Task> GetProjectDiagnosticsForIdsAsync( Project project, - ImmutableArray analyzers, ImmutableHashSet? diagnosticIds, - ImmutableArray documentIds, - bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics, - bool includeProjectNonLocalResult, + AnalyzerFilter analyzerFilter, CancellationToken cancellationToken) { var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var analyzerIds = analyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); var result = await client.TryInvokeAsync>( project, - (service, solution, cancellationToken) => service.ProduceProjectDiagnosticsAsync( - solution, project.Id, analyzerIds, diagnosticIds, documentIds, - includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, includeProjectNonLocalResult, - cancellationToken), + (service, solution, cancellationToken) => service.GetProjectDiagnosticsForIdsAsync( + solution, project.Id, diagnosticIds, analyzerFilter, cancellationToken), cancellationToken).ConfigureAwait(false); - if (!result.HasValue) - return []; + return result.HasValue ? result.Value : []; + } + + return await GetProjectDiagnosticsForIdsInProcessAsync( + project, diagnosticIds, analyzerFilter, cancellationToken).ConfigureAwait(false); + } - return result.Value; + public async Task IsAnyDiagnosticIdDeprioritizedAsync( + Project project, ImmutableArray diagnosticIds, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false); + if (client is not null) + { + var result = await client.TryInvokeAsync( + project, + (service, solution, cancellationToken) => service.IsAnyDiagnosticIdDeprioritizedAsync( + solution, project.Id, diagnosticIds, cancellationToken), + cancellationToken).ConfigureAwait(false); + return result.HasValue && result.Value; } - // Fallback to proccessing in proc. - return await ProduceProjectDiagnosticsInProcessAsync( - project, analyzers, diagnosticIds, documentIds, - includeLocalDocumentDiagnostics, - includeNonLocalDocumentDiagnostics, - includeProjectNonLocalResult, - cancellationToken).ConfigureAwait(false); + return await IsAnyDeprioritizedDiagnosticIdInProcessAsync( + project, diagnosticIds, cancellationToken).ConfigureAwait(false); } - public async Task> ComputeDiagnosticsAsync( + public async Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, - ImmutableArray allAnalyzers, - ImmutableArray syntaxAnalyzers, - ImmutableArray semanticSpanAnalyzers, - ImmutableArray semanticDocumentAnalyzers, - bool incrementalAnalysis, - bool logPerformanceInfo, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, + DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { - if (allAnalyzers.Length == 0) - return []; - var client = await RemoteHostClient.TryGetClientAsync(document.Project, cancellationToken).ConfigureAwait(false); if (client is not null) { - var allAnalyzerIds = allAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var syntaxAnalyzersIds = syntaxAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var semanticSpanAnalyzersIds = semanticSpanAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var semanticDocumentAnalyzersIds = semanticDocumentAnalyzers.Select(a => a.GetAnalyzerId()).ToImmutableHashSet(); - var result = await client.TryInvokeAsync>( document.Project, - (service, solution, cancellationToken) => service.ComputeDiagnosticsAsync( - solution, document.Id, range, - allAnalyzerIds, syntaxAnalyzersIds, semanticSpanAnalyzersIds, semanticDocumentAnalyzersIds, - incrementalAnalysis, logPerformanceInfo, cancellationToken), + (service, solution, cancellationToken) => service.GetDiagnosticsForSpanAsync( + solution, document.Id, range, diagnosticIdFilter, priority, diagnosticKind, cancellationToken), cancellationToken).ConfigureAwait(false); - return result.HasValue ? result.Value : []; } - return await ComputeDiagnosticsInProcessAsync( - document, range, allAnalyzers, syntaxAnalyzers, semanticSpanAnalyzers, semanticDocumentAnalyzers, - incrementalAnalysis, logPerformanceInfo, cancellationToken).ConfigureAwait(false); + return await GetDiagnosticsForSpanInProcessAsync( + document, range, diagnosticIdFilter, priority, diagnosticKind, cancellationToken).ConfigureAwait(false); } } diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DocumentAnalysisExecutor.cs b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DocumentAnalysisExecutor.cs index 42a908bf57f..f8dbe964b25 100644 --- a/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DocumentAnalysisExecutor.cs +++ b/src/roslyn/src/Features/Core/Portable/Diagnostics/Service/DocumentAnalysisExecutor.cs @@ -28,12 +28,11 @@ internal sealed partial class DiagnosticAnalyzerService private sealed partial class DocumentAnalysisExecutor { private readonly DiagnosticAnalyzerService _diagnosticAnalyzerService; - private readonly CompilationWithAnalyzersPair? _compilationWithAnalyzers; + private readonly CompilationWithAnalyzers? _compilationWithAnalyzers; private readonly bool _logPerformanceInfo; private readonly Action? _onAnalysisException; - private readonly ImmutableArray _compilationBasedProjectAnalyzersInAnalysisScope; - private readonly ImmutableArray _compilationBasedHostAnalyzersInAnalysisScope; + private readonly ImmutableArray _compilationBasedAnalyzersInAnalysisScope; private ImmutableDictionary? _lazySyntaxDiagnostics; private ImmutableDictionary? _lazySemanticDiagnostics; @@ -41,7 +40,7 @@ private sealed partial class DocumentAnalysisExecutor public DocumentAnalysisExecutor( DiagnosticAnalyzerService diagnosticAnalyzerService, DocumentAnalysisScope analysisScope, - CompilationWithAnalyzersPair? compilationWithAnalyzers, + CompilationWithAnalyzers? compilationWithAnalyzers, bool logPerformanceInfo, Action? onAnalysisException = null) { @@ -51,14 +50,9 @@ public DocumentAnalysisExecutor( _logPerformanceInfo = logPerformanceInfo; _onAnalysisException = onAnalysisException; - var compilationBasedProjectAnalyzers = compilationWithAnalyzers?.ProjectAnalyzers.ToImmutableHashSet(); - _compilationBasedProjectAnalyzersInAnalysisScope = compilationBasedProjectAnalyzers != null - ? analysisScope.ProjectAnalyzers.WhereAsArray(compilationBasedProjectAnalyzers.Contains) - : []; - - var compilationBasedHostAnalyzers = compilationWithAnalyzers?.HostAnalyzers.ToImmutableHashSet(); - _compilationBasedHostAnalyzersInAnalysisScope = compilationBasedHostAnalyzers != null - ? analysisScope.HostAnalyzers.WhereAsArray(compilationBasedHostAnalyzers.Contains) + var compilationBasedProjectAnalyzers = compilationWithAnalyzers?.Analyzers.ToImmutableHashSet(); + _compilationBasedAnalyzersInAnalysisScope = compilationBasedProjectAnalyzers != null + ? analysisScope.Analyzers.WhereAsArray(compilationBasedProjectAnalyzers.Contains) : []; } @@ -70,10 +64,10 @@ public DocumentAnalysisExecutor With(DocumentAnalysisScope analysisScope) /// /// Return all local diagnostics (syntax, semantic) that belong to given document for the given analyzer by calculating them. /// - public async Task> ComputeDiagnosticsInProcessAsync( + public async ValueTask> ComputeDiagnosticsInProcessAsync( DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(AnalysisScope.ProjectAnalyzers.Contains(analyzer) || AnalysisScope.HostAnalyzers.Contains(analyzer)); + Contract.ThrowIfFalse(AnalysisScope.Analyzers.Contains(analyzer)); var textDocument = AnalysisScope.TextDocument; var span = AnalysisScope.Span; @@ -88,7 +82,7 @@ public async Task> ComputeDiagnosticsInProcessAsy ? null : await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var documentDiagnostics = await ComputeDocumentDiagnosticAnalyzerDiagnosticsAsync( - documentAnalyzer, textDocument, kind, _compilationWithAnalyzers?.HostCompilation, tree, cancellationToken).ConfigureAwait(false); + documentAnalyzer, textDocument, kind, _compilationWithAnalyzers?.Compilation, tree, cancellationToken).ConfigureAwait(false); return Extensions.ConvertToLocalDiagnostics(documentDiagnostics, textDocument, span); } @@ -143,7 +137,7 @@ public async Task> ComputeDiagnosticsInProcessAsy #if DEBUG var diags = await diagnostics.ToDiagnosticsAsync(textDocument.Project, cancellationToken).ConfigureAwait(false); - var compilation = _compilationBasedProjectAnalyzersInAnalysisScope.Contains(analyzer) ? _compilationWithAnalyzers.ProjectCompilation : _compilationWithAnalyzers.HostCompilation; + var compilation = _compilationWithAnalyzers.Compilation; RoslynDebug.AssertNotNull(compilation); Debug.Assert(diags.Length == CompilationWithAnalyzers.GetEffectiveDiagnostics(diags, compilation).Count()); Debug.Assert(diagnostics.Length == Extensions.ConvertToLocalDiagnostics(diags, textDocument, span).Count()); @@ -151,7 +145,7 @@ public async Task> ComputeDiagnosticsInProcessAsy return diagnostics; - async Task> GetAnalysisResultInProcessAsync( + async ValueTask> GetAnalysisResultInProcessAsync( DocumentAnalysisScope analysisScope) { RoslynDebug.Assert(_compilationWithAnalyzers != null); @@ -169,26 +163,22 @@ async Task> Ge } } - async Task> GetCompilerAnalyzerDiagnosticsInProcessAsync(TextSpan? span) + async ValueTask> GetCompilerAnalyzerDiagnosticsInProcessAsync(TextSpan? span) { - RoslynDebug.Assert(analyzer.IsCompilerAnalyzer()); - RoslynDebug.Assert(_compilationWithAnalyzers != null); - RoslynDebug.Assert(_compilationBasedProjectAnalyzersInAnalysisScope.Contains(analyzer) || _compilationBasedHostAnalyzersInAnalysisScope.Contains(analyzer)); - RoslynDebug.Assert(AnalysisScope.TextDocument is Document); + Contract.ThrowIfFalse(analyzer.IsCompilerAnalyzer()); + Contract.ThrowIfNull(_compilationWithAnalyzers); + Contract.ThrowIfFalse(_compilationBasedAnalyzersInAnalysisScope.Contains(analyzer)); + Contract.ThrowIfFalse(AnalysisScope.TextDocument is Document); - var analysisScope = _compilationBasedProjectAnalyzersInAnalysisScope.Contains(analyzer) - ? AnalysisScope.WithAnalyzers([analyzer], []).WithSpan(span) - : AnalysisScope.WithAnalyzers([], [analyzer]).WithSpan(span); + var analysisScope = AnalysisScope.WithAnalyzers([analyzer]).WithSpan(span); var analysisResult = await GetAnalysisResultInProcessAsync(analysisScope).ConfigureAwait(false); - if (!analysisResult.TryGetValue(analyzer, out var result)) - { - return []; - } - return result.GetDocumentDiagnostics(analysisScope.TextDocument.Id, analysisScope.Kind); + return analysisResult.TryGetValue(analyzer, out var result) + ? result.GetDocumentDiagnostics(analysisScope.TextDocument.Id, analysisScope.Kind) + : []; } - async Task> GetSyntaxDiagnosticsInProcessAsync() + async ValueTask> GetSyntaxDiagnosticsInProcessAsync() { // PERF: // 1. Compute diagnostics for all analyzers with a single invocation into CompilationWithAnalyzers. @@ -197,7 +187,7 @@ async Task> GetSyntaxDiagnosticsInProcessAsync() // for rest of the analyzers. This is needed to ensure faster refresh for compiler diagnostics while typing. RoslynDebug.Assert(_compilationWithAnalyzers != null); - RoslynDebug.Assert(_compilationBasedProjectAnalyzersInAnalysisScope.Contains(analyzer) || _compilationBasedHostAnalyzersInAnalysisScope.Contains(analyzer)); + RoslynDebug.Assert(_compilationBasedAnalyzersInAnalysisScope.Contains(analyzer)); if (isCompilerAnalyzer) { @@ -211,7 +201,7 @@ async Task> GetSyntaxDiagnosticsInProcessAsync() { using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"{nameof(GetSyntaxDiagnosticsInProcessAsync)}.{nameof(GetAnalysisResultInProcessAsync)}"); - var analysisScope = AnalysisScope.WithAnalyzers(_compilationBasedProjectAnalyzersInAnalysisScope, _compilationBasedHostAnalyzersInAnalysisScope); + var analysisScope = AnalysisScope.WithAnalyzers(_compilationBasedAnalyzersInAnalysisScope); var syntaxDiagnostics = await GetAnalysisResultInProcessAsync(analysisScope).ConfigureAwait(false); Interlocked.CompareExchange(ref _lazySyntaxDiagnostics, syntaxDiagnostics, null); } @@ -221,7 +211,7 @@ async Task> GetSyntaxDiagnosticsInProcessAsync() : []; } - async Task> GetSemanticDiagnosticsInProcessAsync() + async ValueTask> GetSemanticDiagnosticsInProcessAsync() { // PERF: // 1. Compute diagnostics for all analyzers with a single invocation into CompilationWithAnalyzers. @@ -247,7 +237,7 @@ async Task> GetSemanticDiagnosticsInProcessAsync( { using var _ = TelemetryLogging.LogBlockTimeAggregatedHistogram(FunctionId.RequestDiagnostics_Summary, $"{nameof(GetSemanticDiagnosticsInProcessAsync)}.{nameof(GetAnalysisResultInProcessAsync)}"); - var analysisScope = AnalysisScope.WithAnalyzers(_compilationBasedProjectAnalyzersInAnalysisScope, _compilationBasedHostAnalyzersInAnalysisScope); + var analysisScope = AnalysisScope.WithAnalyzers(_compilationBasedAnalyzersInAnalysisScope); var semanticDiagnostics = await GetAnalysisResultInProcessAsync(analysisScope).ConfigureAwait(false); Interlocked.CompareExchange(ref _lazySemanticDiagnostics, semanticDiagnostics, null); } @@ -257,7 +247,7 @@ async Task> GetSemanticDiagnosticsInProcessAsync( : []; } - async Task GetAdjustedSpanForCompilerAnalyzerAsync(Document document) + async ValueTask GetAdjustedSpanForCompilerAnalyzerAsync(Document document) { // This method is to workaround a bug (https://github.com/dotnet/roslyn/issues/1557) // once that bug is fixed, we should be able to use given span as it is. @@ -293,7 +283,7 @@ async Task> GetSemanticDiagnosticsInProcessAsync( } #if DEBUG - async Task VerifySpanBasedCompilerDiagnosticsAsync(Document document) + async ValueTask VerifySpanBasedCompilerDiagnosticsAsync(Document document) { if (!span.HasValue) { @@ -345,7 +335,7 @@ static bool IsUnusedImportDiagnostic(Diagnostic d) } #endif - async Task> RemapDiagnosticLocationsIfRequiredAsync( + async ValueTask> RemapDiagnosticLocationsIfRequiredAsync( ImmutableArray diagnostics) { if (diagnostics.IsEmpty) diff --git a/src/roslyn/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/roslyn/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 7e765c700fd..add507817b2 100644 --- a/src/roslyn/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/roslyn/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2754,8 +2754,8 @@ private async Task> AnalyzeSemanticsAsync( continue; } - var oldSymbolInNewCompilation = symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; - var newSymbolInOldCompilation = symbolCache.GetKey(newSymbol, cancellationToken).Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var oldSymbolInNewCompilation = symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol; + var newSymbolInOldCompilation = symbolCache.GetKey(newSymbol, cancellationToken).Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol; if (oldSymbolInNewCompilation == null || newSymbolInOldCompilation == null) { @@ -2815,6 +2815,18 @@ private async Task> AnalyzeSemanticsAsync( // Delete/insert/update edit of a member of a reloadable type (including nested types) results in Replace edit of the containing type. // If a Delete edit is part of delete-insert operation (member moved to a different partial type declaration or to a different file) // skip producing Replace semantic edit for this Delete edit as one will be reported by the corresponding Insert edit. + // + // Updates to types nested into reloadable type are handled as Replace edits of the reloadable type. + // + // Rationale: + // Any update to a member of a reloadable type results is a Replace edit of the type. + // Replace edit generates a new version of the entire reloadable type, including any types nested into it. + // Therefore, updating members results in new versions of all types nested in the reloadable type. + // It would be unnecessarily limiting and inconsistent to update nested types "in-place". + // + // Scenario: + // Razor page, which is a reloadable type, may define nested types using @functions block. + // Any changes should be allowed to be made in a Razor page, including changes to nested types defined in @functions block. var oldContainingType = oldSymbol?.ContainingType; var newContainingType = newSymbol?.ContainingType; @@ -2823,31 +2835,30 @@ private async Task> AnalyzeSemanticsAsync( if (containingType != null && (syntacticEditKind != EditKind.Delete || newSymbol == null)) { var containingTypeSymbolKey = symbolCache.GetKey(containingType, cancellationToken); - oldContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; - newContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; - - if (oldContainingType != null && newContainingType != null && IsReloadable(oldContainingType)) + oldContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol; + newContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol; + + if (AddReloadableTypeSemanticEdit( + editScript, + newModel, + diagnostics, + capabilities, + processedSymbols, + semanticEdits, + oldTree, + newTree, + newDeclaration, + oldContainingType, + newContainingType, + cancellationToken)) { - if (processedSymbols.Add(newContainingType)) - { - if (capabilities.GrantNewTypeDefinition(containingType)) - { - semanticEdits.Add(SemanticEditInfo.CreateReplace(containingTypeSymbolKey, - IsPartialTypeEdit(oldContainingType, newContainingType, oldTree, newTree) ? containingTypeSymbolKey : null)); - } - else - { - CreateDiagnosticContext(diagnostics, oldContainingType, newContainingType, newDeclaration, newModel, editScript.Match). - Report(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, cancellationToken); - } - } - continue; } } + // Handle changes to reloadable type itself (the above handles changes to its members and types). // Deleting a reloadable type is a rude edit, reported the same as for non-reloadable. - // Adding a reloadable type is a standard type addition (TODO: unless added to a reloadable type?). + // Adding a reloadable type is a standard type addition (unless added to a reloadable type). // Making reloadable attribute non-reloadable results in a new version of the type that is // not reloadable but does not update the old version in-place. if (syntacticEditKind != EditKind.Delete && oldSymbol is INamedTypeSymbol oldType && newSymbol is INamedTypeSymbol newType && IsReloadable(oldType)) @@ -2967,7 +2978,7 @@ newSymbol is IPropertySymbol newProperty && // If so, skip the member deletion and only report the containing symbol deletion. var oldContainingType = oldSymbol.ContainingType; var containingTypeKey = symbolCache.GetKey(oldContainingType, cancellationToken); - var newContainingType = (INamedTypeSymbol?)containingTypeKey.Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var newContainingType = (INamedTypeSymbol?)containingTypeKey.Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol; if (newContainingType == null) { // If a type parameter is deleted from the parameter list of a type declaration, the symbol key won't be resolved (because the arities do not match). @@ -3092,7 +3103,7 @@ newSymbol is IPropertySymbol newProperty && HasEdit(editMap, GetSymbolDeclarationSyntax(newAssociatedMember, cancellationToken), EditKind.Insert); var containingTypeKey = symbolCache.GetKey(newContainingType, cancellationToken); - oldContainingType = containingTypeKey.Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol as INamedTypeSymbol; + oldContainingType = containingTypeKey.Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol as INamedTypeSymbol; // Check rude edits for each member even if it is inserted into a new type. if (!hasAssociatedSymbolInsert && IsMember(newSymbol)) @@ -3507,23 +3518,20 @@ IFieldSymbol or var oldContainingType = oldSymbol.ContainingType; var newContainingType = newSymbol.ContainingType; - if (oldContainingType != null && newContainingType != null && IsReloadable(oldContainingType)) + if (AddReloadableTypeSemanticEdit( + editScript, + newModel, + diagnostics, + capabilities, + processedSymbols, + semanticEdits, + oldTree, + newTree, + newDeclaration, + oldContainingType, + newContainingType, + cancellationToken)) { - if (processedSymbols.Add(newContainingType)) - { - if (capabilities.GrantNewTypeDefinition(newContainingType)) - { - var oldContainingTypeKey = SymbolKey.Create(oldContainingType, cancellationToken); - semanticEdits.Add(SemanticEditInfo.CreateReplace(oldContainingTypeKey, - IsPartialTypeEdit(oldContainingType, newContainingType, oldTree, newTree) ? oldContainingTypeKey : null)); - } - else - { - CreateDiagnosticContext(diagnostics, oldContainingType, newContainingType, newDeclaration, newModel, editScript.Match) - .Report(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, cancellationToken); - } - } - continue; } @@ -3629,7 +3637,7 @@ bool PreprocessSymbolEdit(ref ISymbol? oldSymbol, ref ISymbol? newSymbol) static ISymbol? Resolve(ISymbol symbol, SymbolKey symbolKey, Compilation compilation, CancellationToken cancellationToken) { // Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation. - var result = symbolKey.Resolve(compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var result = symbolKey.Resolve(compilation, cancellationToken: cancellationToken).Symbol; // If we were looking for a definition and an implementation is returned the definition does not exist. return symbol.IsPartialImplementation() && result?.IsPartialDefinition() == true ? null : result; @@ -3709,7 +3717,7 @@ protected static bool IsMemberOrDelegate(ISymbol symbol) protected static ISymbol? GetSemanticallyMatchingNewSymbol(ISymbol? oldSymbol, ISymbol? newSymbol, Compilation newCompilation, SymbolInfoCache symbolCache, CancellationToken cancellationToken) => oldSymbol != null && IsMember(oldSymbol) && newSymbol != null && IsMember(newSymbol) && - symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol is { } matchingNewSymbol && + symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newCompilation, cancellationToken: cancellationToken).Symbol is { } matchingNewSymbol && !matchingNewSymbol.IsSynthesized() && matchingNewSymbol != newSymbol ? matchingNewSymbol @@ -3805,7 +3813,7 @@ void AddEdits(IMethodSymbol? constructor, Compilation otherCompilation, bool isD IsPrimaryConstructor(constructor, cancellationToken) && constructor.GetMatchingDeconstructor() is { IsImplicitlyDeclared: true } deconstructor) { - if (SymbolKey.Create(deconstructor, cancellationToken).Resolve(otherCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol != null) + if (SymbolKey.Create(deconstructor, cancellationToken).Resolve(otherCompilation, cancellationToken: cancellationToken).Symbol != null) { // Update for transition from synthesized to declared deconstructor AddUpdateEditsForMemberAndAccessors(semanticEdits, deconstructor, cancellationToken); @@ -4053,7 +4061,7 @@ void AddDelete(ISymbol? symbol) continue; } - var newType = SymbolKey.Create(oldType, cancellationToken).Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var newType = SymbolKey.Create(oldType, cancellationToken).Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol; if (newType == null) { var key = (oldType.Name, oldType.Arity); @@ -4084,7 +4092,7 @@ void AddDelete(ISymbol? symbol) continue; } - var oldType = SymbolKey.Create(newType, cancellationToken).Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var oldType = SymbolKey.Create(newType, cancellationToken).Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol; if (oldType == null) { // Check if a type with the same name and arity was also removed. If so treat it as a move. @@ -4148,6 +4156,49 @@ private static bool HasRestartRequiredAttribute(ISymbol symbol) private static bool IsReloadable(INamedTypeSymbol type) => TypeOrBaseTypeHasCompilerServicesAttribute(type, CreateNewOnMetadataUpdateAttributeName); + private static INamedTypeSymbol? TryGetOutermostReloadableType(INamedTypeSymbol type) + => type.GetContainingTypesAndThis().FirstOrDefault(IsReloadable); + + private bool AddReloadableTypeSemanticEdit( + EditScript editScript, + DocumentSemanticModel newModel, + RudeEditDiagnosticsBuilder diagnostics, + EditAndContinueCapabilitiesGrantor capabilities, + PooledHashSet processedSymbols, + ArrayBuilder semanticEdits, + SyntaxTree oldTree, + SyntaxTree newTree, + SyntaxNode? newDeclaration, + INamedTypeSymbol? oldContainingType, + INamedTypeSymbol? newContainingType, + CancellationToken cancellationToken) + { + if (oldContainingType is null || + newContainingType is null || + TryGetOutermostReloadableType(oldContainingType) is not { } oldOutermostReloadableType || + TryGetOutermostReloadableType(newContainingType) is not { } newOutermostReloadableType) + { + return false; + } + + if (processedSymbols.Add(newOutermostReloadableType)) + { + if (capabilities.GrantNewTypeDefinition(newOutermostReloadableType)) + { + var oldOutermostReloadableTypeKey = SymbolKey.Create(oldOutermostReloadableType, cancellationToken); + semanticEdits.Add(SemanticEditInfo.CreateReplace(oldOutermostReloadableTypeKey, + IsPartialTypeEdit(oldOutermostReloadableType, newOutermostReloadableType, oldTree, newTree) ? oldOutermostReloadableTypeKey : null)); + } + else + { + CreateDiagnosticContext(diagnostics, oldOutermostReloadableType, newOutermostReloadableType, newDeclaration, newModel, editScript.Match). + Report(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, cancellationToken); + } + } + + return true; + } + private static bool TypeOrBaseTypeHasCompilerServicesAttribute(INamedTypeSymbol type, string attributeName) { var current = type; @@ -5193,7 +5244,7 @@ private SyntaxNode GetDiagnosticNode(out int distance, CancellationToken cancell while (oldContainer is not null and not INamespaceSymbol { IsGlobalNamespace: true }) { var symbolKey = SymbolKey.Create(oldSymbol, cancellationToken); - if (symbolKey.Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol is { } newSymbol) + if (symbolKey.Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol is { } newSymbol) { return newSymbol; } @@ -5600,7 +5651,7 @@ private void AddConstructorEdits( } else { - var resolution = newCtorKey.Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken); + var resolution = newCtorKey.Resolve(oldModel.Compilation, cancellationToken: cancellationToken); // There may be semantic errors in the compilation that result in multiple candidates. // Pick the first candidate. @@ -7054,7 +7105,7 @@ private bool DeleteEditImpliesInsertEdit(ISymbol oldSymbol, ISymbol newSymbol, C GetPrimaryConstructor(newSymbol.ContainingType, cancellationToken) is { } newPrimaryConstructor && method.HasDeconstructorSignature(newPrimaryConstructor)) { - var oldConstructor = SymbolKey.Create(newPrimaryConstructor, cancellationToken).Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var oldConstructor = SymbolKey.Create(newPrimaryConstructor, cancellationToken).Resolve(oldCompilation, cancellationToken: cancellationToken).Symbol; // An insert exists if the new primary constructor is explicitly declared and // the old one doesn't exist, is synthesized, or is not a primary constructor parameter. @@ -7067,7 +7118,7 @@ private bool DeleteEditImpliesInsertEdit(ISymbol oldSymbol, ISymbol newSymbol, C GetPrimaryConstructor(newSymbol.ContainingType, cancellationToken)?.Parameters.FirstOrDefault( static (parameter, name) => parameter.Name == name, newSymbol.Name) is { } newPrimaryParameter) { - var oldParameter = SymbolKey.Create(newPrimaryParameter, cancellationToken).Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var oldParameter = SymbolKey.Create(newPrimaryParameter, cancellationToken).Resolve(oldCompilation, cancellationToken: cancellationToken).Symbol; var oldProperty = (IPropertySymbol)oldSymbol; // An insert exists if the new primary parameter is explicitly declared and diff --git a/src/roslyn/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/roslyn/src/Features/Core/Portable/EditAndContinue/EditSession.cs index 46f4185b7bb..bc72391f66c 100644 --- a/src/roslyn/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/roslyn/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -841,7 +841,7 @@ internal static void MergePartialEdits( SymbolKeyResolution oldResolution; if (edit.Kind is SemanticEditKind.Update or SemanticEditKind.Delete) { - oldResolution = edit.Symbol.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken); + oldResolution = edit.Symbol.Resolve(oldCompilation, cancellationToken: cancellationToken); Contract.ThrowIfNull(oldResolution.Symbol); } else @@ -852,13 +852,13 @@ internal static void MergePartialEdits( SymbolKeyResolution newResolution; if (edit.Kind is SemanticEditKind.Update or SemanticEditKind.Insert or SemanticEditKind.Replace) { - newResolution = edit.Symbol.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken); + newResolution = edit.Symbol.Resolve(newCompilation, cancellationToken: cancellationToken); Contract.ThrowIfNull(newResolution.Symbol); } else if (edit.Kind == SemanticEditKind.Delete && edit.DeletedSymbolContainer is not null) { // For deletes, we use NewSymbol to reference the containing type of the deleted member - newResolution = edit.DeletedSymbolContainer.Value.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken); + newResolution = edit.DeletedSymbolContainer.Value.Resolve(newCompilation, cancellationToken: cancellationToken); Contract.ThrowIfNull(newResolution.Symbol); } else diff --git a/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs b/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs index c0c37364579..1d0d1fb5f5d 100644 --- a/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs +++ b/src/roslyn/src/Features/Core/Portable/EditAndContinue/RudeEditDiagnosticsBuilder.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; @@ -44,6 +44,6 @@ public void Add(RudeEditDiagnostic diagnostic, RudeEditReportingCondition? defer public ImmutableArray GetAllDiagnostics(Func includeDeferred) => [ .. Diagnostics, - .. DeferredDiagnostics.Where(item => includeDeferred(item.diagnostic, item.condition)).Select(static item => item.diagnostic) + .. DeferredDiagnostics.SelectAsArray(item => includeDeferred(item.diagnostic, item.condition), static item => item.diagnostic) ]; } diff --git a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs index ac98e96d93c..aec0a39acb1 100644 --- a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs +++ b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs @@ -28,7 +28,7 @@ internal sealed class DateAndTimeLanguageDetector( public ImmutableArray LanguageIdentifiers => ["Date", "Time", "DateTime", "DateTimeFormat"]; public DateAndTimeLanguageDetector Create(Compilation compilation, EmbeddedLanguageInfo info) - => new DateAndTimeLanguageDetector(info, compilation); + => new(info, compilation); } private const string FormatName = "format"; diff --git a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index 37359943592..514fc9a7cc6 100644 --- a/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/roslyn/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -421,9 +421,16 @@ private bool CheckDescendants( var syntaxFacts = Info.SyntaxFacts; var node = syntaxFacts.WalkUpParentheses(token.GetRequiredParent()); - // if we're inside some collection-like initializer, find the instance actually being created. if (syntaxFacts.IsAnyInitializerExpression(node.Parent, out var instance)) + { + // if we're inside some initializer (like `new[] { "..." }`), find the instance actually being created. node = syntaxFacts.WalkUpParentheses(instance); + } + else if (syntaxFacts.IsExpressionElement(node.Parent)) + { + // if we're inside some collection (like `["..."]`), find the instance actually being created. + node = syntaxFacts.WalkUpParentheses(node.Parent.GetRequiredParent()); + } return node; } diff --git a/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs b/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs index f83c05f0c51..d31ae5870b2 100644 --- a/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs +++ b/src/roslyn/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncWorkItemQueue.cs @@ -20,7 +20,7 @@ private abstract class UnitTestingAsyncWorkItemQueue(UnitTestingSolutionCr where TKey : class { private readonly object _gate = new(); - private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(initialCount: 0); + private readonly SemaphoreSlim _semaphore = new(initialCount: 0); private bool _disposed; private readonly UnitTestingSolutionCrawlerProgressReporter _progressReporter = progressReporter; diff --git a/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs b/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs index a61907085fa..b1ff54fcd1f 100644 --- a/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs +++ b/src/roslyn/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs @@ -227,7 +227,7 @@ private OperationStatus GetOperationStatus( { var readonlyFieldStatus = CheckReadOnlyFields(symbolMap); - var namesWithAnonymousTypes = variables.Where(v => v.OriginalTypeHadAnonymousTypeOrDelegate).Select(v => v.Name ?? string.Empty); + var namesWithAnonymousTypes = variables.SelectAsArray(v => v.OriginalTypeHadAnonymousTypeOrDelegate, v => v.Name ?? string.Empty); if (returnTypeHasAnonymousType) { namesWithAnonymousTypes = namesWithAnonymousTypes.Concat("return type"); diff --git a/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index 604a4f3d190..939d588a580 100644 --- a/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/roslyn/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -252,7 +252,7 @@ private static void AddFoldersToNamespaceContainers(List container, ILis if (folders != null && folders.Count != 0) { // Remove the empty entries and replace the spaces in the folder name to '_' - var refinedFolders = folders.Where(n => n != null && !n.IsEmpty()).Select(n => n.Replace(' ', '_')).ToArray(); + var refinedFolders = folders.SelectAsArray(n => n != null && !n.IsEmpty(), n => n.Replace(' ', '_')); container.AddRange(refinedFolders); } } diff --git a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs index c2febd44789..f6190e6cba5 100644 --- a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs +++ b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs @@ -105,21 +105,18 @@ void AddHintsIfAppropriate(SyntaxNode node) if (HintMatches(kind, literalParameters, objectCreationParameters, otherParameters)) { - var inlineHintText = GetReplacementText(parameter.Name); var textSpan = new TextSpan(position, 0); - TextChange? replacementTextChange = null; - if (!parameter.IsParams) - { - replacementTextChange = new TextChange(textSpan, inlineHintText); - } + TextChange? replacementTextChange = parameter.IsParams + ? null + : new TextChange(textSpan, GetReplacementText(parameter.Name)); result.Add(new InlineHint( textSpan, [new TaggedText(TextTags.Text, parameter.Name + ": ")], replacementTextChange, ranking: InlineHintsConstants.ParameterRanking, - InlineHintHelpers.GetDescriptionFunction(position, parameter.GetSymbolKey(cancellationToken: cancellationToken), displayOptions))); + InlineHintHelpers.GetDescriptionFunction(position, parameter, displayOptions))); } } } diff --git a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs index e7f24a25ae4..3a3744256ba 100644 --- a/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs +++ b/src/roslyn/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs @@ -95,7 +95,7 @@ public async Task> GetInlineHintsAsync( result.Add(new InlineHint( span, taggedText, textChange, ranking: InlineHintsConstants.TypeRanking, - InlineHintHelpers.GetDescriptionFunction(spanStart, type.GetSymbolKey(cancellationToken), displayOptions))); + InlineHintHelpers.GetDescriptionFunction(spanStart, type, displayOptions))); } return result.ToImmutableAndClear(); diff --git a/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs b/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs index 3f7c1c32c7e..f2d0fbe097e 100644 --- a/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs +++ b/src/roslyn/src/Features/Core/Portable/InlineHints/InlineHintHelpers.cs @@ -16,13 +16,16 @@ namespace Microsoft.CodeAnalysis.InlineHints; internal static class InlineHintHelpers { - public static Func>>? GetDescriptionFunction(int position, SymbolKey symbolKey, SymbolDescriptionOptions options) - => (document, cancellationToken) => GetDescriptionAsync(document, position, symbolKey, options, cancellationToken); + public static Func>>? GetDescriptionFunction(int position, ISymbol symbol, SymbolDescriptionOptions options) + { + return (document, cancellationToken) => GetDescriptionAsync(document, position, symbol, options, cancellationToken); + } - private static async Task> GetDescriptionAsync(Document document, int position, SymbolKey symbolKey, SymbolDescriptionOptions options, CancellationToken cancellationToken) + private static async Task> GetDescriptionAsync(Document document, int position, ISymbol originalSymbol, SymbolDescriptionOptions options, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var symbolKey = originalSymbol.GetSymbolKey(cancellationToken); var symbol = symbolKey.Resolve(semanticModel.Compilation, cancellationToken: cancellationToken).Symbol; if (symbol != null) { diff --git a/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs b/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs index 737a21de6cc..3d7fecf6936 100644 --- a/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/Intents/IntentDataProvider.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Features.Intents; internal sealed class IntentDataProvider( string? serializedIntentData) { - private static readonly Lazy s_serializerOptions = new Lazy(() => + private static readonly Lazy s_serializerOptions = new(() => { var serializerOptions = new JsonSerializerOptions { diff --git a/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs b/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs index 954bb8fcd3f..a1cf4fd32c2 100644 --- a/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs +++ b/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,11 +18,12 @@ namespace Microsoft.CodeAnalysis.IntroduceVariable; internal abstract partial class AbstractIntroduceVariableService { - private sealed partial class State(TService service, SemanticDocument document, CodeCleanupOptions options) + private sealed partial class State( + TService service, SemanticDocument document, CodeCleanupOptions options, TExpressionSyntax expression) { public SemanticDocument Document { get; } = document; public CodeCleanupOptions Options { get; } = options; - public TExpressionSyntax Expression { get; private set; } + public TExpressionSyntax Expression { get; } = expression; public bool InAttributeContext { get; private set; } public bool InBlockContext { get; private set; } @@ -38,34 +37,35 @@ private sealed partial class State(TService service, SemanticDocument document, public bool IsConstant { get; private set; } - private SemanticMap _semanticMap; + private SemanticMap? _semanticMap; private readonly TService _service = service; - public static async Task GenerateAsync( + public static async Task GenerateAsync( TService service, SemanticDocument document, CodeCleanupOptions options, TextSpan textSpan, CancellationToken cancellationToken) { - var state = new State(service, document, options); - if (!await state.TryInitializeAsync(document, textSpan, cancellationToken).ConfigureAwait(false)) - { + var expression = await document.Document.TryGetRelevantNodeAsync(textSpan, cancellationToken).ConfigureAwait(false); + if (expression is null) + return null; + + var state = new State(service, document, options, expression); + if (!state.TryInitialize(document, textSpan, cancellationToken)) return null; - } return state; } - private async Task TryInitializeAsync( + private bool TryInitialize( SemanticDocument document, TextSpan textSpan, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - Expression = await document.Document.TryGetRelevantNodeAsync(textSpan, cancellationToken).ConfigureAwait(false); - if (Expression == null || CodeRefactoringHelpers.IsNodeUnderselected(Expression, textSpan)) + if (CodeRefactoringHelpers.IsNodeUnderselected(Expression, textSpan)) return false; // Don't introduce constant for another constant. Doesn't apply to sub-expression of constant. @@ -101,17 +101,9 @@ private async Task TryInitializeAsync( if (!CanIntroduceVariable(textSpan.IsEmpty, cancellationToken)) return false; - if (containingType is null) - { - var globalStatement = Expression.AncestorsAndSelf().FirstOrDefault(syntaxFacts.IsGlobalStatement); - if (globalStatement != null) - { - InGlobalStatementContext = true; - return true; - } - + var type = GetTypeSymbol(Document, Expression, cancellationToken, objectAsDefault: false); + if (type == null || type.SpecialType == SpecialType.System_Void) return false; - } IsConstant = IsExpressionConstant(Document, Expression, _service, cancellationToken); @@ -142,17 +134,8 @@ private async Task TryInitializeAsync( var enclosingBlocks = _service.GetContainingExecutableBlocks(Expression); if (enclosingBlocks.Any()) { - // If we're inside a block, then don't even try the other options (like field, - // constructor initializer, etc.). This is desirable behavior. If we're in a - // block in a field, then we're in a lambda, and we want to offer to generate - // a local, and not a field. - if (IsInBlockContext(cancellationToken)) - { - InBlockContext = true; - return true; - } - - return false; + InBlockContext = true; + return true; } // NOTE: All checks from this point forward are intentionally ordered to be AFTER the check for Block Context. @@ -181,6 +164,18 @@ private async Task TryInitializeAsync( return false; } + if (containingType is null) + { + var globalStatement = Expression.AncestorsAndSelf().FirstOrDefault(syntaxFacts.IsGlobalStatement); + if (globalStatement != null) + { + InGlobalStatementContext = true; + return true; + } + + return false; + } + if (CanGenerateInto(cancellationToken)) { if (IsInParameterContext(cancellationToken)) @@ -284,7 +279,7 @@ private bool CanIntroduceVariable( // // In essence, this says "i can be replaced with an expression as long as I'm not being // written to". - var semanticFacts = Document.Project.Services.GetService(); + var semanticFacts = Document.GetRequiredLanguageService(); return semanticFacts.CanReplaceWithRValue(Document.SemanticModel, Expression, cancellationToken); } diff --git a/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs b/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs deleted file mode 100644 index 878de27cc36..00000000000 --- a/src/roslyn/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis; - -namespace Microsoft.CodeAnalysis.IntroduceVariable; - -internal abstract partial class AbstractIntroduceVariableService -{ - private sealed partial class State - { - private bool IsInBlockContext(CancellationToken cancellationToken) - { - if (!IsInTypeDeclarationOrValidCompilationUnit()) - { - return false; - } - - // If refer to a query property, then we use the query context instead. - var bindingMap = GetSemanticMap(cancellationToken); - if (bindingMap.AllReferencedSymbols.Any(s => s is IRangeVariableSymbol)) - { - return false; - } - - var type = GetTypeSymbol(Document, Expression, cancellationToken, objectAsDefault: false); - if (type == null || type.SpecialType == SpecialType.System_Void) - { - return false; - } - - return true; - } - } -} diff --git a/src/roslyn/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs index b8ff00c9f98..e66086a545f 100644 --- a/src/roslyn/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs @@ -31,7 +31,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( CodeAction.Create( FeaturesResources.Invert_conditional, - c => InvertConditionalAsync(document, conditional, c), + cancellationToken => InvertConditionalAsync(document, conditional, cancellationToken), nameof(FeaturesResources.Invert_conditional)), conditional.Span); } diff --git a/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.StatementRange.cs b/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.StatementRange.cs index bedcc5d1388..b94e599c980 100644 --- a/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.StatementRange.cs +++ b/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.StatementRange.cs @@ -8,7 +8,12 @@ namespace Microsoft.CodeAnalysis.InvertIf; internal abstract partial class AbstractInvertIfCodeRefactoringProvider< - TSyntaxKind, TStatementSyntax, TIfStatementSyntax, TEmbeddedStatement> + TSyntaxKind, + TStatementSyntax, + TIfStatementSyntax, + TEmbeddedStatementSyntax, + TDirectiveTriviaSyntax, + TIfDirectiveTriviaSyntax> { protected readonly struct StatementRange { diff --git a/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs index 45959cc1b0c..21fb58c2d7f 100644 --- a/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs @@ -16,16 +16,24 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InvertIf; internal abstract partial class AbstractInvertIfCodeRefactoringProvider< - TSyntaxKind, TStatementSyntax, TIfStatementSyntax, TEmbeddedStatement> : CodeRefactoringProvider + TSyntaxKind, + TStatementSyntax, + TIfStatementSyntax, + TEmbeddedStatementSyntax, + TDirectiveTriviaSyntax, + TIfDirectiveTriviaSyntax> : CodeRefactoringProvider where TSyntaxKind : struct, Enum where TStatementSyntax : SyntaxNode where TIfStatementSyntax : TStatementSyntax + where TDirectiveTriviaSyntax : SyntaxNode + where TIfDirectiveTriviaSyntax : TDirectiveTriviaSyntax { private enum InvertIfStyle { @@ -58,23 +66,25 @@ private enum InvertIfStyle protected abstract bool IsElseless(TIfStatementSyntax ifNode); protected abstract StatementRange GetIfBodyStatementRange(TIfStatementSyntax ifNode); + protected abstract SyntaxNode GetCondition(TIfStatementSyntax ifNode); + protected abstract SyntaxNode GetCondition(TIfDirectiveTriviaSyntax ifNode); - protected abstract IEnumerable UnwrapBlock(TEmbeddedStatement ifBody); - protected abstract TEmbeddedStatement GetIfBody(TIfStatementSyntax ifNode); - protected abstract TEmbeddedStatement GetElseBody(TIfStatementSyntax ifNode); - protected abstract TEmbeddedStatement GetEmptyEmbeddedStatement(); + protected abstract IEnumerable UnwrapBlock(TEmbeddedStatementSyntax ifBody); + protected abstract TEmbeddedStatementSyntax GetIfBody(TIfStatementSyntax ifNode); + protected abstract TEmbeddedStatementSyntax GetElseBody(TIfStatementSyntax ifNode); + protected abstract TEmbeddedStatementSyntax GetEmptyEmbeddedStatement(); - protected abstract TEmbeddedStatement AsEmbeddedStatement( + protected abstract TEmbeddedStatementSyntax AsEmbeddedStatement( IEnumerable statements, - TEmbeddedStatement original); + TEmbeddedStatementSyntax original); protected abstract TIfStatementSyntax UpdateIf( SourceText sourceText, TIfStatementSyntax ifNode, SyntaxNode condition, - TEmbeddedStatement trueStatement, - TEmbeddedStatement? falseStatement = default); + TEmbeddedStatementSyntax trueStatement, + TEmbeddedStatementSyntax? falseStatement = default); protected abstract SyntaxNode WithStatements( SyntaxNode node, @@ -82,8 +92,108 @@ protected abstract SyntaxNode WithStatements( public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { - var (document, _, cancellationToken) = context; + if (await TryComputeRefactoringForIfDirectiveAsync(context).ConfigureAwait(false)) + return; + + await TryComputeRefactorForIfStatementAsync(context).ConfigureAwait(false); + } + + private async ValueTask TryComputeRefactoringForIfDirectiveAsync(CodeRefactoringContext context) + { + var (document, textSpan, cancellationToken) = context; + if (!textSpan.IsEmpty) + return false; + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var token = root.FindToken(textSpan.Start, findInsideTrivia: true); + var ifDirective = token.GetAncestor(); + if (ifDirective is null) + return false; + + if (HasErrorDiagnostics(ifDirective)) + return false; + + var syntaxFacts = document.GetRequiredLanguageService(); + var syntaxKinds = syntaxFacts.SyntaxKinds; + + if (ifDirective.RawKind != syntaxKinds.IfDirectiveTrivia) + return false; + + var conditionalDirectives = syntaxFacts.GetMatchingConditionalDirectives(ifDirective, cancellationToken); + if (conditionalDirectives.Length != 3) + return false; + + if (conditionalDirectives[0] != ifDirective || + conditionalDirectives[1].RawKind != syntaxKinds.ElseDirectiveTrivia || + conditionalDirectives[2].RawKind != syntaxKinds.EndIfDirectiveTrivia) + { + return false; + } + + var elseDirective = (TDirectiveTriviaSyntax)conditionalDirectives[1]; + var endIfDirective = (TDirectiveTriviaSyntax)conditionalDirectives[2]; + + if (HasErrorDiagnostics(elseDirective) || + HasErrorDiagnostics(endIfDirective)) + { + return false; + } + + var title = GetTitle(); + context.RegisterRefactoring(CodeAction.Create( + title, + cancellationToken => InvertIfDirectiveAsync(document, ifDirective, elseDirective, endIfDirective, cancellationToken), + title), + ifDirective.Span); + return true; + } + + private async Task InvertIfDirectiveAsync( + Document document, + TIfDirectiveTriviaSyntax ifDirective, + TDirectiveTriviaSyntax elseDirective, + TDirectiveTriviaSyntax endIfDirective, + CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var generator = document.GetRequiredLanguageService(); + var condition = GetCondition(ifDirective); + var invertedCondition = generator.Negate( + generator.SyntaxGeneratorInternal, + condition, + semanticModel, + cancellationToken); + + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var ifDirectiveLine = text.Lines.GetLineFromPosition(ifDirective.SpanStart); + var elseDirectiveLine = text.Lines.GetLineFromPosition(elseDirective.SpanStart); + var endIfDirectiveLine = text.Lines.GetLineFromPosition(endIfDirective.SpanStart); + + var trueSpanStart = text.Lines[ifDirectiveLine.LineNumber + 1].Start; + var trueSpan = TextSpan.FromBounds(trueSpanStart, Math.Max(trueSpanStart, text.Lines[elseDirectiveLine.LineNumber - 1].SpanIncludingLineBreak.End)); + + var falseSpanStart = text.Lines[elseDirectiveLine.LineNumber + 1].Start; + var falseSpan = TextSpan.FromBounds(falseSpanStart, Math.Max(falseSpanStart, text.Lines[endIfDirectiveLine.LineNumber - 1].SpanIncludingLineBreak.End)); + + // Swap the condition with the new condition. + // Swap the true/false sections. + var newText = text.WithChanges( + new TextChange(condition.FullSpan, invertedCondition.ToFullString()), + new TextChange(trueSpan, text.ToString(falseSpan)), + new TextChange(falseSpan, text.ToString(trueSpan))); + + var updatedDocument = document.WithText(newText); + return updatedDocument; + } + + private static bool HasErrorDiagnostics(SyntaxNode node) + => node.GetDiagnostics().Any(static d => d.Severity == DiagnosticSeverity.Error); + + private async ValueTask TryComputeRefactorForIfStatementAsync(CodeRefactoringContext context) + { + var (document, textSpan, cancellationToken) = context; var ifNode = await context.TryGetRelevantNodeAsync().ConfigureAwait(false); if (ifNode == null) return; @@ -92,11 +202,10 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex return; var title = GetTitle(); - context.RegisterRefactoring( - CodeAction.Create( - title, - c => InvertIfAsync(document, ifNode, c), - title), + context.RegisterRefactoring(CodeAction.Create( + title, + cancellationToken => InvertIfAsync(document, ifNode, cancellationToken), + title), ifNode.Span); } diff --git a/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs b/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs index a8e57e9361c..20230877da8 100644 --- a/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs +++ b/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/AbstractStructuralTypeDisplayService.cs @@ -66,16 +66,12 @@ public StructuralTypeDisplayInfo GetTypeDisplayInfo( int position) { if (directStructuralTypeReferences.Length == 0) - { - return new StructuralTypeDisplayInfo( - SpecializedCollections.EmptyDictionary(), - SpecializedCollections.EmptyList()); - } + return StructuralTypeDisplayInfo.Empty; var transitiveStructuralTypeReferences = GetTransitiveStructuralTypeReferences(directStructuralTypeReferences); transitiveStructuralTypeReferences = OrderStructuralTypes(transitiveStructuralTypeReferences, orderSymbol); - IList typeParts = []; + using var _ = ArrayBuilder.GetInstance(out var typeParts); if (transitiveStructuralTypeReferences.Length > 0) { @@ -112,10 +108,10 @@ public StructuralTypeDisplayInfo GetTypeDisplayInfo( // Finally, assign a name to all the anonymous types. var structuralTypeToName = GenerateStructuralTypeNames(transitiveStructuralTypeReferences); - typeParts = StructuralTypeDisplayInfo.ReplaceStructuralTypes( - typeParts, structuralTypeToName, semanticModel, position); + var typePartsArray = StructuralTypeDisplayInfo.ReplaceStructuralTypes( + typeParts.ToImmutableAndClear(), structuralTypeToName, semanticModel, position); - return new StructuralTypeDisplayInfo(structuralTypeToName, typeParts); + return new StructuralTypeDisplayInfo(structuralTypeToName, typePartsArray); } private static Dictionary GenerateStructuralTypeNames( diff --git a/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs b/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs index 813d2eff75a..9784d4e6333 100644 --- a/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs +++ b/src/roslyn/src/Features/Core/Portable/LanguageServices/AnonymousTypeDisplayService/StructuralTypeDisplayInfo.cs @@ -3,28 +3,26 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.LanguageService; -internal readonly struct StructuralTypeDisplayInfo +internal readonly struct StructuralTypeDisplayInfo( + IDictionary structuralTypeToName, + ImmutableArray typesParts) { - public IDictionary StructuralTypeToName { get; } - public IList TypesParts { get; } + public static readonly StructuralTypeDisplayInfo Empty = default; - public StructuralTypeDisplayInfo( - IDictionary structuralTypeToName, - IList typesParts) - : this() - { - StructuralTypeToName = structuralTypeToName; - TypesParts = typesParts; - } + public IDictionary StructuralTypeToName => structuralTypeToName ?? SpecializedCollections.EmptyDictionary(); + public ImmutableArray TypesParts => typesParts.NullToEmpty(); - public IList ReplaceStructuralTypes(IList parts, SemanticModel semanticModel, int position) + public ImmutableArray ReplaceStructuralTypes(ImmutableArray parts, SemanticModel semanticModel, int position) => ReplaceStructuralTypes(parts, StructuralTypeToName, semanticModel, position); - public static IList ReplaceStructuralTypes( - IList parts, + public static ImmutableArray ReplaceStructuralTypes( + ImmutableArray parts, IDictionary structuralTypeToName, SemanticModel semanticModel, int position) @@ -37,14 +35,14 @@ public static IList ReplaceStructuralTypes( } public static bool ReplaceStructuralTypes( - IList parts, + ImmutableArray parts, IDictionary structuralTypeToName, SemanticModel semanticModel, int position, - out List newParts) + out ImmutableArray newParts) { var changed = false; - newParts = []; + using var _ = ArrayBuilder.GetInstance(out var newPartsBuilder); foreach (var part in parts) { @@ -53,7 +51,7 @@ public static bool ReplaceStructuralTypes( if (structuralTypeToName.TryGetValue(type, out var name) && part.ToString() != name) { - newParts.Add(new SymbolDisplayPart(part.Kind, symbol: null, name)); + newPartsBuilder.Add(new SymbolDisplayPart(part.Kind, symbol: null, name)); changed = true; continue; } @@ -62,15 +60,22 @@ public static bool ReplaceStructuralTypes( if (type.IsTupleType && part.ToString() == "") { var displayParts = type.ToMinimalDisplayParts(semanticModel, position); - newParts.AddRange(displayParts); + newPartsBuilder.AddRange(displayParts); changed = true; continue; } } - newParts.Add(part); + newPartsBuilder.Add(part); + } + + if (!changed) + { + newParts = []; + return false; } - return changed; + newParts = newPartsBuilder.ToImmutableAndClear(); + return true; } } diff --git a/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index ce70ce3816c..8f9c1c2549e 100644 --- a/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -80,7 +80,7 @@ protected abstract partial class AbstractSymbolDescriptionBuilder private readonly SemanticModel _semanticModel; private readonly int _position; - private readonly Dictionary> _groupMap = []; + private readonly Dictionary> _groupMap = []; private readonly Dictionary> _documentationMap = []; private readonly Func _getNavigationHint; @@ -450,7 +450,7 @@ private async Task AddDescriptionPartAsync(ISymbol symbol) private ImmutableArray BuildDescription(SymbolDescriptionGroups groups) { - var finalParts = new List(); + using var _ = ArrayBuilder.GetInstance(out var finalParts); var orderedGroups = _groupMap.Keys.OrderBy((g1, g2) => g1 - g2); foreach (var group in orderedGroups) @@ -460,14 +460,13 @@ private ImmutableArray BuildDescription(SymbolDescriptionGrou continue; } - if (!finalParts.IsEmpty()) + if (!finalParts.IsEmpty) { var newLines = GetPrecedingNewLineCount(group); finalParts.AddRange(LineBreak(newLines)); } - var parts = _groupMap[group]; - finalParts.AddRange(parts); + finalParts.AddRange(_groupMap[group]); } return finalParts.AsImmutable(); @@ -833,16 +832,12 @@ protected void AddToGroup(SymbolDescriptionGroups group, params SymbolDisplayPar protected void AddToGroup(SymbolDescriptionGroups group, params IEnumerable[] partsArray) { - var partsList = partsArray.Flatten().ToList(); - if (partsList.Count > 0) + var partsList = partsArray.Flatten().ToImmutableArray(); + if (partsList.Length > 0) { - if (!_groupMap.TryGetValue(group, out var existingParts)) - { - existingParts = []; - _groupMap.Add(group, existingParts); - } - - existingParts.AddRange(partsList); + var existingParts = _groupMap.TryGetValue(group, out var temp) ? temp : []; + existingParts = existingParts.AddRange(partsList); + _groupMap[group] = existingParts; } } diff --git a/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AnonymousTypes.cs b/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AnonymousTypes.cs index 91405a4dc23..64d8eae5566 100644 --- a/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AnonymousTypes.cs +++ b/src/roslyn/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AnonymousTypes.cs @@ -33,7 +33,7 @@ where part.Symbol.IsAnonymousType() || part.Symbol.IsTupleType() var info = LanguageServices.GetRequiredService().GetTypeDisplayInfo( firstSymbol, directStructuralTypes.ToImmutableArrayOrEmpty(), _semanticModel, _position); - if (info.TypesParts.Count > 0) + if (info.TypesParts.Length > 0) AddToGroup(SymbolDescriptionGroups.StructuralTypes, info.TypesParts); foreach (var (group, parts) in _groupMap.ToArray()) diff --git a/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs b/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs index ee971b2a1fa..61fafff3af9 100644 --- a/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs +++ b/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedMethodSymbol.cs @@ -121,6 +121,8 @@ public IMethodSymbol ReduceExtensionMethod(ITypeSymbol receiverType) return _symbol.ReduceExtensionMethod(receiverType); } + public IMethodSymbol AssociatedExtensionImplementation => null; + public bool IsVararg => _symbol.IsVararg; public bool IsCheckedBuiltin => _symbol.IsCheckedBuiltin; diff --git a/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs b/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs index 716d3f41362..60d4933b760 100644 --- a/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs +++ b/src/roslyn/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs @@ -144,8 +144,9 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public bool IsNativeIntegerType => _symbol.IsNativeIntegerType; public bool IsExtension => _symbol.IsExtension; - public IParameterSymbol ExtensionParameter => _symbol.ExtensionParameter; + public string ExtensionGroupingName => _symbol.ExtensionGroupingName; + public string ExtensionMarkerName => _symbol.ExtensionMarkerName; public bool IsFileLocal => _symbol.IsFileLocal; diff --git a/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs b/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs index 11fe4ab91a4..21bc683ee77 100644 --- a/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs +++ b/src/roslyn/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs @@ -119,6 +119,14 @@ protected override async Task> ComputeOperation var projectToLocations = memberReferenceLocations.ToLookup(loc => loc.location.Document.Project.Id); var solutionWithFixedReferences = await RefactorReferencesAsync(projectToLocations, newDoc.Project.Solution, newType, typeArgIndices, cancellationToken).ConfigureAwait(false); + // Qualify static member references in the moved members + solutionWithFixedReferences = await QualifyStaticMemberReferencesAsync( + solutionWithFixedReferences, + sourceDoc.Id, + memberNodes, + _selectedType, + moveOptions.SelectedMembers, + cancellationToken).ConfigureAwait(false); sourceDoc = solutionWithFixedReferences.GetRequiredDocument(sourceDoc.Id); // get back nodes from our changes @@ -194,6 +202,15 @@ private static async Task RefactorAndMoveAsync( var projectToLocations = memberReferenceLocations.ToLookup(loc => loc.location.Document.Project.Id); var solutionWithFixedReferences = await RefactorReferencesAsync(projectToLocations, oldSolution, newType, typeArgIndices, cancellationToken).ConfigureAwait(false); + // Qualify static member references in the moved members + var originalType = selectedMembers.First().ContainingType; + solutionWithFixedReferences = await QualifyStaticMemberReferencesAsync( + solutionWithFixedReferences, + sourceDocId, + oldMemberNodes, + originalType, + selectedMembers, + cancellationToken).ConfigureAwait(false); var sourceDoc = solutionWithFixedReferences.GetRequiredDocument(sourceDocId); // get back tracked nodes from our changes @@ -371,4 +388,63 @@ private static async Task FixReferencesSingleDocumentAsync( .Select(loc => (loc, refSymbol.Definition.IsExtensionMethod()))) .ToImmutableArrayOrEmpty(); } + + /// + /// Process the bodies of members being moved to qualify references to static members + /// that remain in the original class. + /// + private static async Task QualifyStaticMemberReferencesAsync( + Solution solution, + DocumentId sourceDocId, + ImmutableArray memberNodes, + INamedTypeSymbol originalType, + ImmutableArray selectedMembers, + CancellationToken cancellationToken) + { + var sourceDoc = solution.GetRequiredDocument(sourceDocId); + var root = await sourceDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await sourceDoc.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = sourceDoc.GetRequiredLanguageService(); + var generator = SyntaxGenerator.GetGenerator(sourceDoc); + + var members = memberNodes + .Select(node => root.GetCurrentNode(node)) + .WhereNotNull(); + var nodesToUpdate = new Dictionary(); + + // Only walking into the members being moved since those are the only places where we need to qualify + // references to static members from the original class that are not being moved. + foreach (var memberNode in members) + { + foreach (var identifierNode in memberNode.DescendantNodes().Where( + n => syntaxFacts.IsIdentifierName(n) && + !syntaxFacts.IsNameOfAnyMemberAccessExpression(n))) + { + var symbolInfo = semanticModel.GetSymbolInfo(identifierNode, cancellationToken); + var symbol = symbolInfo.Symbol; + + // Check if it's a static member from the original class that isn't being moved + if (symbol is { IsStatic: true, ContainingType: { } containingType } && + symbol.ContainingType.Name == originalType.Name && + !selectedMembers.Contains(symbol)) + { + var qualified = generator.MemberAccessExpression( + generator.TypeExpression(originalType), + identifierNode); + + nodesToUpdate.Add(identifierNode, qualified); + } + } + } + + if (nodesToUpdate.Count > 0) + { + root = root.ReplaceNodes(nodesToUpdate.Keys, + (original, _) => nodesToUpdate[original]); + + solution = solution.WithDocumentSyntaxRoot(sourceDocId, root); + } + + return solution; + } } diff --git a/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs b/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs index 5e24d607d0b..49016549712 100644 --- a/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs +++ b/src/roslyn/src/Features/Core/Portable/NavigateTo/INavigateToSearcherHost.cs @@ -54,7 +54,7 @@ internal sealed class DefaultNavigateToSearchHost( public async ValueTask IsFullyLoadedAsync(CancellationToken cancellationToken) { - var workspaceService = _solution.Workspace.Services.GetService(); + var workspaceService = _solution.Services.GetService(); if (workspaceService != null) return await workspaceService.IsFullyLoadedAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs b/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs index 53c832fc623..026aafeb92c 100644 --- a/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/OrganizeImports/OrganizeImportsCodeRefactoringProvider.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.OrganizeImports; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class OrganizeImportsCodeRefactoringProvider() : SyntaxEditorBasedCodeRefactoringProvider { - protected override ImmutableArray SupportedFixAllScopes => [FixAllScope.Project, FixAllScope.Solution]; + protected override ImmutableArray SupportedRefactorAllScopes => [RefactorAllScope.Project, RefactorAllScope.Solution]; /// /// Matches 'remove unnecessary imports' code fix. This way we don't show 'sort imports' above it. @@ -51,7 +51,7 @@ protected override CodeActionRequestPriority ComputeRequestPriority() return (oldRoot, newRoot); } - protected override async Task FixAllAsync( + protected override async Task RefactorAllAsync( Document document, ImmutableArray fixAllSpans, SyntaxEditor editor, string? equivalenceKey, CancellationToken cancellationToken) { var (oldRoot, newRoot) = await RemoveImportsAsync(document, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs b/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs index 143ce4f9222..0b8e86a57ae 100644 --- a/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs +++ b/src/roslyn/src/Features/Core/Portable/PdbSourceDocument/ImplementationAssemblyLookupService.cs @@ -22,7 +22,7 @@ internal sealed class ImplementationAssemblyLookupService : IImplementationAssem { // We need to generate the namespace name in the same format that is used in metadata, which // is SymbolDisplayFormat.QualifiedNameOnlyFormat, which this is a copy of. - private static readonly SymbolDisplayFormat s_metadataSymbolDisplayFormat = new SymbolDisplayFormat( + private static readonly SymbolDisplayFormat s_metadataSymbolDisplayFormat = new( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); diff --git a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs index b7c786a4671..321f15910b4 100644 --- a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs +++ b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQConstructedType.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Features.RQName.Nodes; internal sealed class RQConstructedType(RQUnconstructedType definingType, IList typeArguments) : RQType { public readonly RQUnconstructedType DefiningType = definingType; - public readonly ReadOnlyCollection TypeArguments = new ReadOnlyCollection(typeArguments); + public readonly ReadOnlyCollection TypeArguments = new(typeArguments); public override SimpleTreeNode ToSimpleTree() { diff --git a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs index 73a731d39ad..17d84712121 100644 --- a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs +++ b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQMethodOrProperty.cs @@ -16,7 +16,7 @@ internal abstract class RQMethodOrProperty( IList parameters) : RQMethodPropertyOrEvent(containingType, memberName) { public readonly int TypeParameterCount = typeParameterCount; - public readonly ReadOnlyCollection Parameters = new ReadOnlyCollection(parameters); + public readonly ReadOnlyCollection Parameters = new(parameters); protected override void AppendChildren(List childList) { diff --git a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs index e6dce7e84cf..e5f9be47133 100644 --- a/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs +++ b/src/roslyn/src/Features/Core/Portable/RQName/Nodes/RQUnconstructedType.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Features.RQName.Nodes; internal sealed class RQUnconstructedType(IList namespaceNames, IList typeInfos) : RQTypeOrNamespace(namespaceNames) { - public readonly ReadOnlyCollection TypeInfos = new ReadOnlyCollection(typeInfos); + public readonly ReadOnlyCollection TypeInfos = new(typeInfos); protected override string RQKeyword { diff --git a/src/roslyn/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/roslyn/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index b51083fab8f..76547cc878e 100644 --- a/src/roslyn/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/roslyn/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -231,7 +231,7 @@ public static DocumentationComment GetAppropriateDocumentationComment(this ISymb public static Func> GetDocumentationPartsFactory( this ISymbol symbol, SemanticModel semanticModel, int position, IDocumentationCommentFormattingService formatter) - => c => symbol.GetDocumentationParts(semanticModel, position, formatter, cancellationToken: c); + => cancellationToken => symbol.GetDocumentationParts(semanticModel, position, formatter, cancellationToken); public static readonly SymbolDisplayFormat CrefFormat = new( diff --git a/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs b/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs index 2a6e78acfc3..d12319bb6dd 100644 --- a/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs +++ b/src/roslyn/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs @@ -38,9 +38,9 @@ protected AbstractSignatureHelpProvider() protected abstract Task GetItemsWorkerAsync(Document document, int position, SignatureHelpTriggerInfo triggerInfo, MemberDisplayOptions options, CancellationToken cancellationToken); protected static SignatureHelpItems? CreateSignatureHelpItems( - IList items, TextSpan applicableSpan, SignatureHelpState? state, int? selectedItemIndex, int parameterIndexOverride) + ImmutableArray items, TextSpan applicableSpan, SignatureHelpState? state, int? selectedItemIndex, int parameterIndexOverride) { - if (items is null || items.Count == 0 || state == null) + if (items.IsDefaultOrEmpty || state == null) return null; if (selectedItemIndex < 0) @@ -70,7 +70,7 @@ protected AbstractSignatureHelpProvider() } protected static SignatureHelpItems? CreateCollectionInitializerSignatureHelpItems( - IList items, TextSpan applicableSpan, SignatureHelpState? state) + ImmutableArray items, TextSpan applicableSpan, SignatureHelpState? state) { // We will have added all the accessible '.Add' methods that take at least one // arguments. However, in general the one-arg Add method is the least likely for the @@ -94,15 +94,15 @@ protected AbstractSignatureHelpProvider() items, applicableSpan, state, items.IndexOf(i => i.Parameters.Length >= 2), parameterIndexOverride: -1); } - private static (IList items, int? selectedItem) Filter(IList items, ImmutableArray parameterNames, int? selectedItem) + private static (ImmutableArray items, int? selectedItem) Filter(ImmutableArray items, ImmutableArray parameterNames, int? selectedItem) { if (parameterNames.IsDefault) - return (items.ToList(), selectedItem); + return (items, selectedItem); - var filteredList = items.Where(i => Include(i, parameterNames)).ToList(); - var isEmpty = filteredList.Count == 0; + var filteredList = items.WhereAsArray(i => Include(i, parameterNames)); + var isEmpty = filteredList.IsEmpty; if (!selectedItem.HasValue || isEmpty) - return (isEmpty ? [.. items] : filteredList, selectedItem); + return (isEmpty ? items : filteredList, selectedItem); // adjust the selected item var selection = items[selectedItem.Value]; @@ -138,11 +138,11 @@ protected SignatureHelpItem CreateItem( IStructuralTypeDisplayService structuralTypeDisplayService, bool isVariadic, Func> documentationFactory, - IList prefixParts, - IList separatorParts, - IList suffixParts, - IList parameters, - IList? descriptionParts = null) + ImmutableArray prefixParts, + ImmutableArray separatorParts, + ImmutableArray suffixParts, + ImmutableArray parameters, + ImmutableArray? descriptionParts = null) { return CreateItem(orderSymbol, semanticModel, position, structuralTypeDisplayService, isVariadic, documentationFactory, prefixParts, separatorParts, suffixParts, parameters, descriptionParts); @@ -155,11 +155,11 @@ protected static SignatureHelpItem CreateItem( IStructuralTypeDisplayService structuralTypeDisplayService, bool isVariadic, Func>? documentationFactory, - IList prefixParts, - IList separatorParts, - IList suffixParts, - IList parameters, - IList? descriptionParts = null) + ImmutableArray prefixParts, + ImmutableArray separatorParts, + ImmutableArray suffixParts, + ImmutableArray parameters, + ImmutableArray? descriptionParts = null) { return CreateItemImpl(orderSymbol, semanticModel, position, structuralTypeDisplayService, isVariadic, documentationFactory, prefixParts, separatorParts, suffixParts, parameters, descriptionParts); @@ -172,15 +172,13 @@ protected static SignatureHelpItem CreateItemImpl( IStructuralTypeDisplayService structuralTypeDisplayService, bool isVariadic, Func>? documentationFactory, - IList prefixParts, - IList separatorParts, - IList suffixParts, - IList parameters, - IList? descriptionParts) + ImmutableArray prefixParts, + ImmutableArray separatorParts, + ImmutableArray suffixParts, + ImmutableArray parameters, + ImmutableArray? descriptionParts) { - descriptionParts = descriptionParts == null - ? SpecializedCollections.EmptyList() - : descriptionParts; + descriptionParts ??= []; var allParts = prefixParts.Concat(separatorParts) .Concat(suffixParts) @@ -195,11 +193,11 @@ where part.Symbol.IsAnonymousType() || part.Symbol.IsTupleType() var info = structuralTypeDisplayService.GetTypeDisplayInfo( orderSymbol, [.. structuralTypes], semanticModel, position); - if (info.TypesParts.Count > 0) + if (info.TypesParts.Length > 0) { var structuralTypeParts = new List { - new SymbolDisplayPart(SymbolDisplayPartKind.Space, null, "\r\n\r\n") + new(SymbolDisplayPartKind.Space, null, "\r\n\r\n") }; structuralTypeParts.AddRange(info.TypesParts); @@ -260,7 +258,7 @@ private static SignatureHelpSymbolParameter ReplaceStructuralTypes( var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var compilation = semanticModel.Compilation; - var finalItems = new List(); + using var _1 = ArrayBuilder.GetInstance(out var finalItems); foreach (var item in itemsForCurrentDocument.Items) { if (item is not SymbolKeySignatureHelpItem symbolKeyItem || @@ -278,7 +276,7 @@ symbolKeyItem.SymbolKey is not SymbolKey symbolKey || symbolKey = SymbolKey.Create(methodSymbol.OriginalDefinition, cancellationToken); } - using var _ = ArrayBuilder.GetInstance(out var invalidProjectsForCurrentSymbol); + using var _2 = ArrayBuilder.GetInstance(out var invalidProjectsForCurrentSymbol); foreach (var relatedDocument in relatedDocuments) { // Try to resolve symbolKey in each related compilation, @@ -295,7 +293,7 @@ symbolKeyItem.SymbolKey is not SymbolKey symbolKey || } return new SignatureHelpItems( - finalItems, + finalItems.ToImmutableAndClear(), itemsForCurrentDocument.ApplicableSpan, itemsForCurrentDocument.SemanticParameterIndex, itemsForCurrentDocument.SyntacticArgumentCount, diff --git a/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpItems.cs b/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpItems.cs index 179ea67ea04..c135184eaba 100644 --- a/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpItems.cs +++ b/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpItems.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SignatureHelp; @@ -16,7 +16,7 @@ internal sealed class SignatureHelpItems /// /// The list of items to present to the user. /// - public IList Items { get; } + public ImmutableArray Items { get; } /// /// The span this session applies to. @@ -59,23 +59,22 @@ internal sealed class SignatureHelpItems public int? SelectedItemIndex { get; } public SignatureHelpItems( - IList items, + ImmutableArray items, TextSpan applicableSpan, int semanticParameterIndex, int syntacticArgumentCount, string? argumentName, int? selectedItem = null) { - Contract.ThrowIfNull(items); - Contract.ThrowIfTrue(items.IsEmpty()); - Contract.ThrowIfTrue(selectedItem.HasValue && selectedItem.Value >= items.Count); + Contract.ThrowIfTrue(items.IsDefaultOrEmpty); + Contract.ThrowIfTrue(selectedItem.HasValue && selectedItem.Value >= items.Length); if (semanticParameterIndex < 0) throw new ArgumentException($"{nameof(semanticParameterIndex)} < 0. {semanticParameterIndex} < 0", nameof(semanticParameterIndex)); // Adjust the `selectedItem` index if duplicates are able to be removed. - var distinctItems = items.Distinct().ToList(); - if (selectedItem.HasValue && items.Count != distinctItems.Count) + var distinctItems = items.Distinct(); + if (selectedItem.HasValue && items.Length != distinctItems.Length) { // `selectedItem` index has already been determined to be valid, it now needs to be adjusted to point // to the equivalent item in the reduced list to account for duplicates being removed diff --git a/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpParameter.cs b/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpParameter.cs index 94250b831e9..f4066e7a45c 100644 --- a/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpParameter.cs +++ b/src/roslyn/src/Features/Core/Portable/SignatureHelp/SignatureHelpParameter.cs @@ -4,10 +4,8 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SignatureHelp; @@ -18,13 +16,13 @@ namespace Microsoft.CodeAnalysis.SignatureHelp; /// point to TaggedText parts. /// internal sealed class SignatureHelpSymbolParameter( - string name, + string? name, bool isOptional, Func>? documentationFactory, - IEnumerable displayParts, - IEnumerable? prefixDisplayParts = null, - IEnumerable? suffixDisplayParts = null, - IEnumerable? selectedDisplayParts = null) + ImmutableArray displayParts, + ImmutableArray? prefixDisplayParts = null, + ImmutableArray? suffixDisplayParts = null, + ImmutableArray? selectedDisplayParts = null) { /// /// The name of this parameter. @@ -40,18 +38,18 @@ internal sealed class SignatureHelpSymbolParameter( /// /// Display parts to show before the normal display parts for the parameter. /// - public IList PrefixDisplayParts { get; } = prefixDisplayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray PrefixDisplayParts { get; } = prefixDisplayParts.NullToEmpty(); /// /// Display parts to show after the normal display parts for the parameter. /// - public IList SuffixDisplayParts { get; } = suffixDisplayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray SuffixDisplayParts { get; } = suffixDisplayParts ?? []; /// /// Display parts for this parameter. This should normally be presented to the user as part /// of the entire signature display. /// - public IList DisplayParts { get; } = displayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray DisplayParts { get; } = displayParts.NullToEmpty(); /// /// True if this parameter is optional or not. Optional parameters may be presented in a @@ -63,16 +61,12 @@ internal sealed class SignatureHelpSymbolParameter( /// Display parts for this parameter that should be presented to the user when this /// parameter is selected. /// - public IList SelectedDisplayParts { get; } = selectedDisplayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray SelectedDisplayParts { get; } = selectedDisplayParts ?? []; private static readonly Func> s_emptyDocumentationFactory = _ => []; - internal IEnumerable GetAllParts() - { - return PrefixDisplayParts.Concat(DisplayParts) - .Concat(SuffixDisplayParts) - .Concat(SelectedDisplayParts); - } + internal ImmutableArray GetAllParts() + => [.. PrefixDisplayParts, .. DisplayParts, .. SuffixDisplayParts, .. SelectedDisplayParts]; public static explicit operator SignatureHelpParameter(SignatureHelpSymbolParameter parameter) { @@ -86,13 +80,13 @@ public static explicit operator SignatureHelpParameter(SignatureHelpSymbolParame } internal sealed class SignatureHelpParameter( - string name, + string? name, bool isOptional, Func>? documentationFactory, - IEnumerable displayParts, - IEnumerable? prefixDisplayParts = null, - IEnumerable? suffixDisplayParts = null, - IEnumerable? selectedDisplayParts = null) + ImmutableArray displayParts, + ImmutableArray? prefixDisplayParts = null, + ImmutableArray? suffixDisplayParts = null, + ImmutableArray? selectedDisplayParts = null) { /// /// The name of this parameter. @@ -108,18 +102,18 @@ internal sealed class SignatureHelpParameter( /// /// Display parts to show before the normal display parts for the parameter. /// - public IList PrefixDisplayParts { get; } = prefixDisplayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray PrefixDisplayParts { get; } = prefixDisplayParts ?? []; /// /// Display parts to show after the normal display parts for the parameter. /// - public IList SuffixDisplayParts { get; } = suffixDisplayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray SuffixDisplayParts { get; } = suffixDisplayParts ?? []; /// /// Display parts for this parameter. This should normally be presented to the user as part /// of the entire signature display. /// - public IList DisplayParts { get; } = displayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray DisplayParts { get; } = displayParts.NullToEmpty(); /// /// True if this parameter is optional or not. Optional parameters may be presented in a @@ -131,7 +125,7 @@ internal sealed class SignatureHelpParameter( /// Display parts for this parameter that should be presented to the user when this /// parameter is selected. /// - public IList SelectedDisplayParts { get; } = selectedDisplayParts.ToImmutableArrayOrEmpty(); + public ImmutableArray SelectedDisplayParts { get; } = selectedDisplayParts ?? []; private static readonly Func> s_emptyDocumentationFactory = _ => []; @@ -140,10 +134,10 @@ public SignatureHelpParameter( string name, bool isOptional, Func>? documentationFactory, - IEnumerable displayParts, - IEnumerable? prefixDisplayParts = null, - IEnumerable? suffixDisplayParts = null, - IEnumerable? selectedDisplayParts = null) + ImmutableArray displayParts, + ImmutableArray? prefixDisplayParts = null, + ImmutableArray? suffixDisplayParts = null, + ImmutableArray? selectedDisplayParts = null) : this(name, isOptional, documentationFactory is null ? null : c => documentationFactory(c).ToTaggedText(), displayParts.ToTaggedText(), @@ -153,12 +147,8 @@ public SignatureHelpParameter( { } - internal IEnumerable GetAllParts() - { - return PrefixDisplayParts.Concat(DisplayParts) - .Concat(SuffixDisplayParts) - .Concat(SelectedDisplayParts); - } + internal ImmutableArray GetAllParts() + => [.. PrefixDisplayParts, .. DisplayParts, .. SuffixDisplayParts, .. SelectedDisplayParts]; public override string ToString() { diff --git a/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs b/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs index 77ec758fd09..ed51879072c 100644 --- a/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs +++ b/src/roslyn/src/Features/Core/Portable/Structure/BlockStructureContext.cs @@ -13,7 +13,7 @@ internal readonly struct BlockStructureContext(SyntaxTree syntaxTree, BlockStruc { // We keep our own ObjectPool of ArrayBuilders as we want to use ArrayBuilders for their ability to efficiently create ImmutableArrays, but don't // want the maximum capacity the default pool uses for dropping items from the pool. - private static readonly ObjectPool> _blockSpanArrayBuilderPool = new ObjectPool>(() => []); + private static readonly ObjectPool> _blockSpanArrayBuilderPool = new(() => []); public readonly ArrayBuilder Spans = _blockSpanArrayBuilderPool.Allocate(); diff --git a/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs b/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs index 7d91d365b6b..3cdbe883397 100644 --- a/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs +++ b/src/roslyn/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.Update.cs @@ -98,7 +98,7 @@ private sealed class Updater(SymbolSearchUpdateEngine service, string source, st { private readonly SymbolSearchUpdateEngine _service = service; private readonly string _source = source; - private readonly DirectoryInfo _cacheDirectoryInfo = new DirectoryInfo(Path.Combine( + private readonly DirectoryInfo _cacheDirectoryInfo = new(Path.Combine( localSettingsDirectory, "PackageCache", string.Format(Invariant($"Format{AddReferenceDatabaseTextFileFormatVersion}")))); /// @@ -383,7 +383,7 @@ await RepeatIOAsync( } private static FileInfo GetBinaryFileInfo(FileInfo databaseFileInfo) - => new FileInfo(Path.ChangeExtension(databaseFileInfo.FullName, ".bin")); + => new(Path.ChangeExtension(databaseFileInfo.FullName, ".bin")); private async Task PatchLocalDatabaseAsync(FileInfo databaseFileInfo, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs index 4b638a0eacd..372a18d2605 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs @@ -143,7 +143,7 @@ public TestParameters WithWorkspaceKind(string workspaceKind) private const string AutoGeneratedAnalyzerConfigHeader = @"# auto-generated .editorconfig for code style options"; protected internal abstract string GetLanguage(); - protected ParenthesesOptionsProvider ParenthesesOptionsProvider => new ParenthesesOptionsProvider(this.GetLanguage()); + protected ParenthesesOptionsProvider ParenthesesOptionsProvider => new(this.GetLanguage()); protected abstract ParseOptions GetScriptOptions(); private protected virtual IDocumentServiceProvider GetDocumentServiceProvider() diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs index 75890c76e73..7d730759675 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor_OptionHelpers.cs @@ -35,20 +35,20 @@ internal static (OptionKey2, object) SingleOption(PerLanguageOption2 (new OptionKey2(option, language), codeStyle); internal OptionsCollection Option(Option2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(Option2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; internal OptionsCollection Option(PerLanguageOption2> option, T enabled, NotificationOption2 notification) - => new OptionsCollection(GetLanguage()) { { option, enabled, notification } }; + => new(GetLanguage()) { { option, enabled, notification } }; internal OptionsCollection Option(Option2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; internal OptionsCollection Option(PerLanguageOption2 option, T value) - => new OptionsCollection(GetLanguage()) { { option, value } }; + => new(GetLanguage()) { { option, value } }; internal OptionsCollection Option(PerLanguageOption2> option, CodeStyleOption2 codeStyle) - => new OptionsCollection(GetLanguage()) { { option, codeStyle } }; + => new(GetLanguage()) { { option, codeStyle } }; } diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs index c9549145cfb..82e41be0b17 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionTest_NoEditor.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; @@ -22,7 +23,6 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; @@ -74,7 +74,7 @@ protected abstract CodeRefactoringProvider CreateCodeRefactoringProvider( return (actions, actionToInvoke); var fixAllCodeAction = await GetFixAllFixAsync(actionToInvoke, - refactoring.Provider, document, span, fixAllScope.Value).ConfigureAwait(false); + refactoring.Provider, document, span, fixAllScope.Value.ToRefactorAllScope()).ConfigureAwait(false); if (fixAllCodeAction == null) return ([], null); @@ -86,15 +86,15 @@ private static async Task GetFixAllFixAsync( CodeRefactoringProvider provider, Document document, TextSpan selectionSpan, - FixAllScope scope) + RefactorAllScope scope) { - var fixAllProvider = provider.GetFixAllProvider(); - if (fixAllProvider == null || !fixAllProvider.GetSupportedFixAllScopes().Contains(scope)) + var refactorAllProvider = provider.GetRefactorAllProvider(); + if (refactorAllProvider == null || !refactorAllProvider.GetSupportedRefactorAllScopes().Contains(scope)) return null; - var fixAllState = new FixAllState(fixAllProvider, document, selectionSpan, provider, scope, originalCodeAction); - var fixAllContext = new FixAllContext(fixAllState, CodeAnalysisProgress.None, CancellationToken.None); - return await fixAllProvider.GetFixAsync(fixAllContext).ConfigureAwait(false); + var refactorAllState = new RefactorAllState(refactorAllProvider, document, selectionSpan, provider, scope, originalCodeAction); + var refactorAllContext = new RefactorAllContext(refactorAllState, CodeAnalysisProgress.None, CancellationToken.None); + return await refactorAllProvider.GetRefactoringAsync(refactorAllContext).ConfigureAwait(false); } protected override Task> GetDiagnosticsWorkerAsync(TTestWorkspace workspace, TestParameters parameters) diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs index 0a4513fab90..e3694733a05 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs @@ -137,7 +137,7 @@ public int GetHashCode(Diagnostic obj) internal sealed class Analyzer : DiagnosticAnalyzer, IBuiltInAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("TestId", "Test", "Test", "Test", DiagnosticSeverity.Warning, isEnabledByDefault: true); public bool IsHighPriority => false; diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs index a881c715c40..5bd70ef0faa 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_NoEditor.cs @@ -165,7 +165,7 @@ protected static Document GetDocumentAndSelectSpan(TTestWorkspace workspace, out document, diagnostic.Location.SourceSpan, [diagnostic], - (a, d) => fixes.Add(new CodeFix(document.Project, a, d)), + (a, d) => fixes.Add(new CodeFix(a, d)), CancellationToken.None); await fixer.RegisterCodeFixesAsync(context); diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs index 159284f1504..a120a975fdd 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/Diagnostics/ParenthesesOptionsProvider.cs @@ -21,13 +21,13 @@ public ParenthesesOptionsProvider(string language) } private static readonly CodeStyleOption2 IgnorePreference = - new CodeStyleOption2(ParenthesesPreference.AlwaysForClarity, NotificationOption2.None); + new(ParenthesesPreference.AlwaysForClarity, NotificationOption2.None); private static readonly CodeStyleOption2 RequireForPrecedenceClarityPreference = - new CodeStyleOption2(ParenthesesPreference.AlwaysForClarity, NotificationOption2.Suggestion); + new(ParenthesesPreference.AlwaysForClarity, NotificationOption2.Suggestion); private static readonly CodeStyleOption2 RemoveIfUnnecessaryPreference = - new CodeStyleOption2(ParenthesesPreference.NeverIfUnnecessary, NotificationOption2.Suggestion); + new(ParenthesesPreference.NeverIfUnnecessary, NotificationOption2.Suggestion); private static IEnumerable>> GetAllExceptOtherParenthesesOptions() { diff --git a/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs b/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs index b6e3fafdda8..9009533e623 100644 --- a/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs +++ b/src/roslyn/src/Features/DiagnosticsTestUtilities/NamingStyles/NamingStylesTestOptionSets.cs @@ -41,67 +41,67 @@ internal OptionsCollection MergeStyles(OptionsCollection first, OptionsCollectio } internal OptionsCollection ClassNamesArePascalCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ClassNamesArePascalCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ClassNamesArePascalCaseOption() } }; internal OptionsCollection FieldNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseOption() } }; internal OptionsCollection FieldNamesAreCamelCaseWithUnderscorePrefix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithUnderscorePrefixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithUnderscorePrefixOption() } }; internal OptionsCollection FieldNamesAreCamelCaseWithFieldUnderscorePrefix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixOption() } }; internal OptionsCollection FieldNamesAreCamelCaseWithFieldUnderscorePrefixAndUnderscoreEndSuffix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixAndUnderscoreEndSuffixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, FieldNamesAreCamelCaseWithFieldUnderscorePrefixAndUnderscoreEndSuffixOption() } }; internal OptionsCollection MethodNamesArePascalCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption() } }; internal OptionsCollection MethodNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesAreCamelCaseOption() } }; internal OptionsCollection ParameterNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseOption() } }; internal OptionsCollection ParameterNamesAreCamelCaseWithPUnderscorePrefix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixOption() } }; internal OptionsCollection ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffix - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffixOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ParameterNamesAreCamelCaseWithPUnderscorePrefixAndUnderscoreEndSuffixOption() } }; internal OptionsCollection LocalNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, LocalNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, LocalNamesAreCamelCaseOption() } }; internal OptionsCollection LocalFunctionNamesAreCamelCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, LocalFunctionNamesAreCamelCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, LocalFunctionNamesAreCamelCaseOption() } }; internal OptionsCollection PropertyNamesArePascalCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, PropertyNamesArePascalCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, PropertyNamesArePascalCaseOption() } }; internal OptionsCollection InterfaceNamesStartWithI - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, InterfaceNamesStartWithIOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, InterfaceNamesStartWithIOption() } }; internal OptionsCollection TypeParameterNamesStartWithT - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, TypeParameterNamesStartWithTOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, TypeParameterNamesStartWithTOption() } }; internal OptionsCollection ConstantsAreUpperCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, ConstantsAreUpperCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, ConstantsAreUpperCaseOption() } }; internal OptionsCollection LocalsAreCamelCaseConstantsAreUpperCase - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, LocalsAreCamelCaseConstantsAreUpperCaseOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, LocalsAreCamelCaseConstantsAreUpperCaseOption() } }; internal OptionsCollection AsyncFunctionNamesEndWithAsync - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, AsyncFunctionNamesEndWithAsyncOption() } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, AsyncFunctionNamesEndWithAsyncOption() } }; internal OptionsCollection MethodNamesWithAccessibilityArePascalCase(ImmutableArray accessibilities) - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption(accessibilities) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, MethodNamesArePascalCaseOption(accessibilities) } }; internal OptionsCollection SymbolKindsArePascalCase(ImmutableArray symbolKinds) - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption(symbolKinds) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption(symbolKinds) } }; internal OptionsCollection SymbolKindsArePascalCaseEmpty() - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption([]) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, SymbolKindsArePascalCaseOption([]) } }; internal OptionsCollection SymbolKindsArePascalCase(object symbolOrTypeKind) => SymbolKindsArePascalCase([ToSymbolKindOrTypeKind(symbolOrTypeKind)]); @@ -125,7 +125,7 @@ internal static SymbolSpecification.SymbolKindOrTypeKind ToSymbolKindOrTypeKind( } internal OptionsCollection AccessibilitiesArePascalCase(ImmutableArray accessibilities) - => new OptionsCollection(_languageName) { { NamingStyleOptions.NamingPreferences, AccessibilitiesArePascalCaseOption(accessibilities) } }; + => new(_languageName) { { NamingStyleOptions.NamingPreferences, AccessibilitiesArePascalCaseOption(accessibilities) } }; private static NamingStylePreferences ClassNamesArePascalCaseOption() { diff --git a/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs b/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs index 5e97ef64084..3749cd6fa9b 100644 --- a/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs +++ b/src/roslyn/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs @@ -55,7 +55,7 @@ public bool Contains(AspNetCoreVirtualChar @char) /// public Enumerator GetEnumerator() - => new Enumerator(_virtualCharSequence.GetEnumerator()); + => new(_virtualCharSequence.GetEnumerator()); /// public struct Enumerator : IEnumerator diff --git a/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs b/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs index ff7599a0068..068aec2a6ff 100644 --- a/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs +++ b/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs @@ -342,8 +342,8 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit) expectedOldSymbol = expectedSemanticEdit.SymbolProvider(oldCompilation); expectedNewSymbol = expectedSemanticEdit.SymbolProvider(newCompilation); - Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol); - Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); + Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation).Symbol); + Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation).Symbol); break; case SemanticEditKind.Delete: @@ -352,25 +352,25 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit) // Symbol key will happily resolve to a definition part that has no implementation, so we validate that // differently if (expectedOldSymbol.IsPartialDefinition() && - symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol is ISymbol resolvedSymbol) + symbolKey.Resolve(oldCompilation).Symbol is ISymbol resolvedSymbol) { Assert.Equal(expectedOldSymbol, resolvedSymbol.PartialDefinitionPart()); Assert.Equal(null, resolvedSymbol.PartialImplementationPart()); } else { - Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol); + Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation).Symbol); // When we're deleting a symbol, and have a deleted symbol container, it means the symbol wasn't really deleted, // but rather had its signature changed in some way. Some of those ways, like changing the return type, are not // represented in the symbol key, so the check below would fail, so we skip it. if (expectedSemanticEdit.DeletedSymbolContainerProvider is null) { - Assert.Equal(null, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); + Assert.Equal(null, symbolKey.Resolve(newCompilation).Symbol); } } - var deletedSymbolContainer = actualSemanticEdit.DeletedSymbolContainer?.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol; + var deletedSymbolContainer = actualSemanticEdit.DeletedSymbolContainer?.Resolve(newCompilation).Symbol; AssertEx.AreEqual( deletedSymbolContainer, expectedSemanticEdit.DeletedSymbolContainerProvider?.Invoke(newCompilation), @@ -380,7 +380,7 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit) case SemanticEditKind.Insert or SemanticEditKind.Replace: expectedNewSymbol = expectedSemanticEdit.SymbolProvider(newCompilation); - Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); + Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation).Symbol); break; default: @@ -390,7 +390,7 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit) // Partial types must match: AssertEx.AreEqual( expectedSemanticEdit.PartialType?.Invoke(newCompilation), - actualSemanticEdit.PartialType?.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol, + actualSemanticEdit.PartialType?.Resolve(newCompilation).Symbol, message: $"{message}, {editKind}({expectedNewSymbol ?? expectedOldSymbol}): Partial types do not match"); var expectedSyntaxMap = expectedSemanticEdit.GetSyntaxMap(); diff --git a/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs b/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs index fe27c3d83ce..98e2e11de9b 100644 --- a/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs +++ b/src/roslyn/src/Features/TestUtilities/EditAndContinue/EditAndContinueWorkspaceTestBase.cs @@ -470,7 +470,7 @@ internal static SourceText CreateTextFromFile(string path) } internal static TextSpan GetSpan(string str, string substr) - => new TextSpan(str.IndexOf(substr), substr.Length); + => new(str.IndexOf(substr), substr.Length); internal static void VerifyReadersDisposed(IEnumerable readers) { diff --git a/src/roslyn/src/Features/TestUtilities/Utils/Options.cs b/src/roslyn/src/Features/TestUtilities/Utils/Options.cs index 5fa08b4ec0c..8f2da1f4173 100644 --- a/src/roslyn/src/Features/TestUtilities/Utils/Options.cs +++ b/src/roslyn/src/Features/TestUtilities/Utils/Options.cs @@ -10,6 +10,6 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests; internal static class Options { - internal static readonly CSharpParseOptions Script = new CSharpParseOptions(kind: SourceCodeKind.Script); - internal static readonly CSharpParseOptions Regular = new CSharpParseOptions(kind: SourceCodeKind.Regular); + internal static readonly CSharpParseOptions Script = new(kind: SourceCodeKind.Script); + internal static readonly CSharpParseOptions Regular = new(kind: SourceCodeKind.Regular); } diff --git a/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb b/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb index e54c887c984..e357b43032a 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportFeatureService.vb @@ -32,7 +32,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport Return node.CanAddImportsStatements(allowInHiddenRegions, cancellationToken) End Function - Protected Overrides Function CanAddImportForMethod( + Protected Overrides Function CanAddImportForMember( diagnosticId As String, syntaxFacts As ISyntaxFacts, node As SyntaxNode, @@ -325,43 +325,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport SyntaxFactory.QualifiedName(CreateNameSyntax(nameSpaceParts, index - 1), namePiece)) End Function - Protected Overrides Function IsViableExtensionMethod(method As IMethodSymbol, - expression As SyntaxNode, - semanticModel As SemanticModel, - syntaxFacts As ISyntaxFacts, - cancellationToken As CancellationToken) As Boolean - Dim leftExpressionType As ITypeSymbol - If syntaxFacts.IsInvocationExpression(expression) Then - leftExpressionType = semanticModel.GetEnclosingNamedType(expression.SpanStart, cancellationToken) - Else - Dim leftExpression As SyntaxNode - If TypeOf expression Is ObjectCreationExpressionSyntax Then - leftExpression = expression - Else - leftExpression = syntaxFacts.GetExpressionOfMemberAccessExpression( - expression, allowImplicitTarget:=True) - If leftExpression Is Nothing Then - Return False - End If - End If - - Dim semanticInfo = semanticModel.GetTypeInfo(leftExpression, cancellationToken) - leftExpressionType = semanticInfo.Type - End If - - Return IsViableExtensionMethod(method, leftExpressionType) - End Function - - Protected Overrides Function IsAddMethodContext(node As SyntaxNode, semanticModel As SemanticModel) As Boolean + Protected Overrides Function IsAddMethodContext( + node As SyntaxNode, + semanticModel As SemanticModel, + ByRef objectCreateExpression As SyntaxNode) As Boolean If node.IsKind(SyntaxKind.ObjectCollectionInitializer) Then - Dim objectCreateExpression = node.GetAncestor(Of ObjectCreationExpressionSyntax) - If objectCreateExpression Is Nothing Then - Return False - End If - - Return True + objectCreateExpression = node.GetAncestor(Of ObjectCreationExpressionSyntax) + Return objectCreateExpression IsNot Nothing End If + objectCreateExpression = Nothing Return False End Function End Class diff --git a/src/roslyn/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectInitializerCompletionProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectInitializerCompletionProvider.vb index 34c8e093d7a..c3fb2bdcf1c 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectInitializerCompletionProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectInitializerCompletionProvider.vb @@ -23,11 +23,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Public Sub New() End Sub - Friend Overrides ReadOnly Property Language As String - Get - Return LanguageNames.VisualBasic - End Get - End Property + Friend Overrides ReadOnly Property Language As String = LanguageNames.VisualBasic Protected Overrides Function GetInitializedMembers(tree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As HashSet(Of String) Dim token = tree.FindTokenOnLeftOfPosition(position, cancellationToken) @@ -54,7 +50,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers document As Document, semanticModel As SemanticModel, position As Integer, - cancellationToken As CancellationToken) As Tuple(Of ITypeSymbol, Location) + cancellationToken As CancellationToken) As (Type As ITypeSymbol, location As Location, isObjectInitializer As Boolean)? Dim tree = semanticModel.SyntaxTree If tree.IsInNonUserCode(position, cancellationToken) Then Return Nothing @@ -92,7 +88,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Dim initializerLocation As Location = token.GetLocation() Dim symbolInfo = semanticModel.GetSymbolInfo(objectCreationExpression.Type, cancellationToken) Dim symbol = TryCast(symbolInfo.Symbol, ITypeSymbol) - Return Tuple.Create(symbol, initializerLocation) + Return (symbol, initializerLocation, isObjectInitializer:=True) End Function Public Overrides Function IsInsertionTrigger(text As SourceText, characterPosition As Integer, options As CompletionOptions) As Boolean diff --git a/src/roslyn/src/Features/VisualBasic/Portable/Copilot/VisualBasicCopilotProposalAdjusterService.vb b/src/roslyn/src/Features/VisualBasic/Portable/Copilot/VisualBasicCopilotProposalAdjusterService.vb new file mode 100644 index 00000000000..3584163b23b --- /dev/null +++ b/src/roslyn/src/Features/VisualBasic/Portable/Copilot/VisualBasicCopilotProposalAdjusterService.vb @@ -0,0 +1,26 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Collections.Immutable +Imports System.Composition +Imports System.Threading +Imports Microsoft.CodeAnalysis.Copilot +Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.Text + +Namespace Microsoft.CodeAnalysis.VisualBasic.Copilot + + Friend NotInheritable Class VisualBasicCopilotProposalAdjusterService + Inherits AbstractCopilotProposalAdjusterService + + + + Public Sub New() + End Sub + + Protected Overrides Function AddMissingTokensIfAppropriateAsync(originalDocument As Document, normalizedChanges As ImmutableArray(Of Text.TextChange), cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of TextChange)) + Return SpecializedTasks.Default(Of ImmutableArray(Of TextChange)) + End Function + End Class +End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.vb index a3276a575d0..7641cf10398 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.vb @@ -7,7 +7,13 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.InvertIf Friend MustInherit Class VisualBasicInvertIfCodeRefactoringProvider(Of TIfStatementSyntax As ExecutableStatementSyntax) - Inherits AbstractInvertIfCodeRefactoringProvider(Of SyntaxKind, StatementSyntax, TIfStatementSyntax, SyntaxList(Of StatementSyntax)) + Inherits AbstractInvertIfCodeRefactoringProvider(Of + SyntaxKind, + StatementSyntax, + TIfStatementSyntax, + SyntaxList(Of StatementSyntax), + DirectiveTriviaSyntax, + IfDirectiveTriviaSyntax) Protected NotOverridable Overrides Function GetTitle() As String Return VBFeaturesResources.Invert_If @@ -119,5 +125,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InvertIf Protected NotOverridable Overrides Function IsSingleStatementStatementRange(statementRange As StatementRange) As Boolean Return Not statementRange.IsEmpty AndAlso statementRange.FirstStatement Is statementRange.LastStatement End Function + + Protected Overrides Function GetCondition(ifNode As IfDirectiveTriviaSyntax) As SyntaxNode + Return ifNode.Condition + End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj b/src/roslyn/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj index d3305438222..a4a590c2893 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj +++ b/src/roslyn/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj @@ -24,8 +24,8 @@ - - + + diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb index ed3d8826fd2..ca9c3bd2fa9 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb @@ -5,6 +5,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators @@ -37,7 +38,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Return Nothing End If - Dim items As New List(Of SignatureHelpItem) + Dim items = ArrayBuilder(Of SignatureHelpItem).GetInstance() Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(node, cancellationToken).ConfigureAwait(False) For Each documentation In Await GetIntrinsicOperatorDocumentationAsync(node, document, cancellationToken).ConfigureAwait(False) @@ -49,12 +50,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim syntaxFacts = document.GetLanguageService(Of ISyntaxFactsService) Return CreateSignatureHelpItems( - items, textSpan, + items.ToImmutableAndFree(), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken), selectedItemIndex:=Nothing, parameterIndexOverride:=-1) End Function Friend Shared Function GetSignatureHelpItemForIntrinsicOperator(document As Document, semanticModel As SemanticModel, position As Integer, documentation As AbstractIntrinsicOperatorDocumentation, cancellationToken As CancellationToken) As SignatureHelpItem - Dim parameters As New List(Of SignatureHelpSymbolParameter) + Dim parameters = ArrayBuilder(Of SignatureHelpSymbolParameter).GetInstance() For i = 0 To documentation.ParameterCount - 1 Dim capturedIndex = i @@ -78,7 +79,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp prefixParts:=documentation.PrefixParts, separatorParts:=GetSeparatorParts(), suffixParts:=suffixParts, - parameters:=parameters) + parameters:=parameters.ToImmutableAndFree()) End Function Protected Overridable Function GetCurrentArgumentStateWorker(node As SyntaxNode, position As Integer) As SignatureHelpState diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.vb index 7ff272a7a8f..c2d65fab1d1 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractOrdinaryMethodSignatureHelpProvider.vb @@ -2,13 +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. +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp - friend MustInherit class AbstractOrdinaryMethodSignatureHelpProvider - inherits AbstractVisualBasicSignatureHelpProvider + Friend MustInherit Class AbstractOrdinaryMethodSignatureHelpProvider + Inherits AbstractVisualBasicSignatureHelpProvider Protected Shared Function ConvertMemberGroupMember(document As Document, member As ISymbol, @@ -26,11 +28,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp GetMemberGroupPreambleParts(member, semanticModel, position), GetSeparatorParts(), GetMemberGroupPostambleParts(member, semanticModel, position), - member.GetParameters().Select(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService)).ToList()) + member.GetParameters().SelectAsArray(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService))) End Function - Private Shared Function GetMemberGroupPreambleParts(symbol As ISymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetMemberGroupPreambleParts(symbol As ISymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() AddExtensionPreamble(symbol, result) @@ -44,13 +46,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp result.AddRange(symbol.ToMinimalDisplayParts(semanticModel, position, format)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetMemberGroupPostambleParts(symbol As ISymbol, - semanticModel As SemanticModel, - position As Integer) As IList(Of SymbolDisplayPart) - Dim parts = New List(Of SymbolDisplayPart) + Private Shared Function GetMemberGroupPostambleParts( + symbol As ISymbol, + semanticModel As SemanticModel, + position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() parts.Add(Punctuation(SyntaxKind.CloseParenToken)) If TypeOf symbol Is IMethodSymbol Then @@ -70,7 +73,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parts.AddRange([property].Type.ToMinimalDisplayParts(semanticModel, position)) End If - Return parts + Return parts.ToImmutableAndFree() End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractVisualBasicSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractVisualBasicSignatureHelpProvider.vb index 1421a16fd87..cce684ddaf9 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractVisualBasicSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AbstractVisualBasicSignatureHelpProvider.vb @@ -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. +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.DocumentationComments +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp @@ -34,8 +36,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Return New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, vbCrLf) End Function - Protected Shared Function GetSeparatorParts() As IList(Of SymbolDisplayPart) - Return {Punctuation(SyntaxKind.CommaToken), Space()} + Protected Shared Function GetSeparatorParts() As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(Punctuation(SyntaxKind.CommaToken), Space()) End Function Protected Shared Function Convert(parameter As IParameterSymbol, @@ -48,7 +50,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parameter.ToMinimalDisplayParts(semanticModel, position)) End Function - Protected Shared Sub AddExtensionPreamble(symbol As ISymbol, result As IList(Of SymbolDisplayPart)) + Protected Shared Sub AddExtensionPreamble(symbol As ISymbol, result As ArrayBuilder(Of SymbolDisplayPart)) If symbol.GetOriginalUnreducedDefinition().IsExtensionMethod() Then result.Add(Punctuation(SyntaxKind.LessThanToken)) result.Add(Text(VBFeaturesResources.Extension)) diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AttributeSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AttributeSignatureHelpProvider.vb index e5e5f917768..5e9c39a2553 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AttributeSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/AttributeSignatureHelpProvider.vb @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -84,8 +85,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim symbolInfo = semanticModel.GetSymbolInfo(attribute, cancellationToken) Dim selectedItem = TryGetSelectedIndex(accessibleConstructors, symbolInfo.Symbol) - Return CreateSignatureHelpItems(accessibleConstructors.Select( - Function(c) Convert(c, within, attribute, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)).ToList(), + Return CreateSignatureHelpItems(accessibleConstructors.SelectAsArray( + Function(c) Convert(c, within, attribute, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken), selectedItem, parameterIndexOverride:=-1) End Function @@ -109,7 +110,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim position = attribute.SpanStart Dim namedParameters = constructor.ContainingType.GetAttributeNamedParameters(semanticModel.Compilation, within). OrderBy(Function(s) s.Name). - ToList() + ToImmutableArray() Dim isVariadic = constructor.Parameters.Length > 0 AndAlso constructor.Parameters.Last().IsParams AndAlso namedParameters.Count = 0 @@ -126,12 +127,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Return item End Function - Private Shared Function GetParameters(constructor As IMethodSymbol, - semanticModel As SemanticModel, - position As Integer, - namedParameters As List(Of ISymbol), - documentationCommentFormattingService As IDocumentationCommentFormattingService) As IList(Of SignatureHelpSymbolParameter) - Dim result = New List(Of SignatureHelpSymbolParameter) + Private Shared Function GetParameters( + constructor As IMethodSymbol, + semanticModel As SemanticModel, + position As Integer, + namedParameters As ImmutableArray(Of ISymbol), + documentationCommentFormattingService As IDocumentationCommentFormattingService) As ImmutableArray(Of SignatureHelpSymbolParameter) + Dim result = ArrayBuilder(Of SignatureHelpSymbolParameter).GetInstance() For Each parameter In constructor.Parameters result.Add(Convert(parameter, semanticModel, position, documentationCommentFormattingService)) @@ -144,7 +146,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp DirectCast(namedParameter, IFieldSymbol).Type, DirectCast(namedParameter, IPropertySymbol).Type) - Dim displayParts = New List(Of SymbolDisplayPart) + Dim displayParts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() displayParts.Add(New SymbolDisplayPart( If(TypeOf namedParameter Is IFieldSymbol, SymbolDisplayPartKind.FieldName, SymbolDisplayPartKind.PropertyName), @@ -156,34 +158,33 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp namedParameter.Name, isOptional:=True, documentationFactory:=namedParameter.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), - displayParts:=displayParts, + displayParts:=displayParts.ToImmutableAndFree(), prefixDisplayParts:=GetParameterPrefixDisplayParts(i))) Next - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetParameterPrefixDisplayParts(i As Integer) As List(Of SymbolDisplayPart) + Private Shared Function GetParameterPrefixDisplayParts(i As Integer) As ImmutableArray(Of SymbolDisplayPart) If i = 0 Then - Return New List(Of SymbolDisplayPart) From { + Return ImmutableArray.Create( New SymbolDisplayPart(SymbolDisplayPartKind.Text, Nothing, FeaturesResources.Properties), Punctuation(SyntaxKind.ColonToken), - Space() - } + Space()) End If Return Nothing End Function - Private Shared Function GetPreambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetPreambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() result.AddRange(method.ContainingType.ToMinimalDisplayParts(semanticModel, position)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetPostambleParts() As IList(Of SymbolDisplayPart) - Return {Punctuation(SyntaxKind.CloseParenToken)} + Private Shared Function GetPostambleParts() As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(Punctuation(SyntaxKind.CloseParenToken)) End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/CollectionInitializerSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/CollectionInitializerSignatureHelpProvider.vb index 56acafe0aa1..1f79cf7cb1e 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/CollectionInitializerSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/CollectionInitializerSignatureHelpProvider.vb @@ -60,7 +60,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False) Return CreateCollectionInitializerSignatureHelpItems( - addMethods.Select(Function(s) ConvertMemberGroupMember(document, s, collectionInitializer.OpenBraceToken.SpanStart, semanticModel)).ToList(), + addMethods.SelectAsArray(Function(s) ConvertMemberGroupMember(document, s, collectionInitializer.OpenBraceToken.SpanStart, semanticModel)), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken)) End Function diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/FunctionAggregationSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/FunctionAggregationSignatureHelpProvider.vb index f99edead13e..fe949363061 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/FunctionAggregationSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/FunctionAggregationSignatureHelpProvider.vb @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -86,7 +87,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim syntaxFacts = document.GetLanguageService(Of ISyntaxFactsService) Return CreateSignatureHelpItems( - accessibleMethods.Select(Function(m) Convert(m, functionAggregation, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)).ToList(), + accessibleMethods.SelectAsArray(Function(m) Convert(m, functionAggregation, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken), selectedItemIndex:=Nothing, parameterIndexOverride:=-1) End Function @@ -108,18 +109,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Return item End Function - Private Shared Function GetPreambleParts(method As IMethodSymbol) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetPreambleParts(method As IMethodSymbol) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() AddExtensionPreamble(method, result) result.AddMethodName(method.Name) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetPostambleParts(method As IMethodSymbol, - semanticModel As SemanticModel, - position As Integer) As IList(Of SymbolDisplayPart) - Dim parts = New List(Of SymbolDisplayPart) + Private Shared Function GetPostambleParts( + method As IMethodSymbol, + semanticModel As SemanticModel, + position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() parts.Add(Punctuation(SyntaxKind.CloseParenToken)) If Not method.ReturnsVoid Then @@ -129,14 +131,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parts.AddRange(method.ReturnType.ToMinimalDisplayParts(semanticModel, position)) End If - Return parts + Return parts.ToImmutableAndFree() End Function - Private Shared Function GetParameterParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer, - documentationCommentFormattingService As IDocumentationCommentFormattingService) As IList(Of SignatureHelpSymbolParameter) + Private Shared Function GetParameterParts( + method As IMethodSymbol, + semanticModel As SemanticModel, + position As Integer, + documentationCommentFormattingService As IDocumentationCommentFormattingService) As ImmutableArray(Of SignatureHelpSymbolParameter) ' Function () As If method.Parameters.Length <> 1 Then - Return SpecializedCollections.EmptyList(Of SignatureHelpSymbolParameter)() + Return ImmutableArray(Of SignatureHelpSymbolParameter).Empty End If ' Function (selector as Func(Of T, R)) As R @@ -148,7 +153,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp delegateInvokeMethod.Parameters.Length = 1 AndAlso Not delegateInvokeMethod.ReturnsVoid Then - Dim parts = New List(Of SymbolDisplayPart) + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() parts.Add(Text(VBWorkspaceResources.expression)) parts.Add(Space()) parts.Add(Keyword(SyntaxKind.AsKeyword)) @@ -159,13 +164,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp VBWorkspaceResources.expression, parameter.IsOptional, parameter.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), - parts) + parts.ToImmutableAndFree()) - Return {sigHelpParameter} + Return ImmutableArray.Create(sigHelpParameter) End If End If - Return SpecializedCollections.EmptyList(Of SignatureHelpSymbolParameter)() + Return ImmutableArray(Of SignatureHelpSymbolParameter).Empty End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.Method.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.Method.vb index 85fed14828d..ee66f712558 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.Method.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.Method.vb @@ -2,12 +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. -Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.PooledObjects +Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Partial Friend Class GenericNameSignatureHelpProvider - - Private Shared Function GetPreambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetPreambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() AddExtensionPreamble(method, result) @@ -22,7 +23,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp result.Add(Punctuation(SyntaxKind.OpenParenToken)) result.Add(Keyword(SyntaxKind.OfKeyword)) result.Add(Space()) - Return result + Return result.ToImmutableAndFree() End Function Private Shared Function GetContainingType(method As IMethodSymbol) As ITypeSymbol @@ -35,8 +36,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp End If End Function - Private Shared Function GetPostambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetPostambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() result.Add(Punctuation(SyntaxKind.CloseParenToken)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) @@ -60,7 +61,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp result.AddRange(method.ReturnType.ToMinimalDisplayParts(semanticModel, position)) End If - Return result + Return result.ToImmutableAndFree() End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.NamedType.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.NamedType.vb index 48240e10b87..4dc0f506b40 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.NamedType.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.NamedType.vb @@ -2,12 +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. +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.PooledObjects + Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Partial Friend Class GenericNameSignatureHelpProvider - Private Shared Function GetPreambleParts(namedType As INamedTypeSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetPreambleParts(namedType As INamedTypeSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() Dim format = New SymbolDisplayFormat( memberOptions:=SymbolDisplayMemberOptions.IncludeContainingType, miscellaneousOptions:=SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers Or SymbolDisplayMiscellaneousOptions.UseSpecialTypes) @@ -15,11 +18,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp result.Add(Punctuation(SyntaxKind.OpenParenToken)) result.Add(Keyword(SyntaxKind.OfKeyword)) result.Add(Space()) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetPostambleParts() As IList(Of SymbolDisplayPart) - Return {Punctuation(SyntaxKind.CloseParenToken)} + Private Shared Function GetPostambleParts() As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(Punctuation(SyntaxKind.CloseParenToken)) End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb index 1650b1b6f68..ca768d60b3e 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb @@ -8,6 +8,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -107,7 +108,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim _syntaxFacts = document.GetLanguageService(Of ISyntaxFactsService) Return CreateSignatureHelpItems( - accessibleSymbols.Select(Function(s) Convert(s, genericName, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)).ToList(), + accessibleSymbols.SelectAsArray(Function(s) Convert(s, genericName, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)), textSpan, GetCurrentArgumentState(root, position, _syntaxFacts, textSpan, cancellationToken), selectedItemIndex:=Nothing, parameterIndexOverride:=-1) End Function @@ -124,7 +125,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp GetPreambleParts(namedType, semanticModel, position), GetSeparatorParts(), GetPostambleParts(), - namedType.TypeParameters.Select(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService)).ToList()) + namedType.TypeParameters.SelectAsArray(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService))) Else Dim method = DirectCast(symbol, IMethodSymbol) item = CreateItem( @@ -135,7 +136,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp GetPreambleParts(method, semanticModel, position), GetSeparatorParts(), GetPostambleParts(method, semanticModel, position), - method.TypeParameters.Select(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService)).ToList()) + method.TypeParameters.SelectAsArray(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService))) End If Return item @@ -144,7 +145,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Private Shared ReadOnly s_minimallyQualifiedFormat As SymbolDisplayFormat = SymbolDisplayFormat.MinimallyQualifiedFormat.WithGenericsOptions(SymbolDisplayFormat.MinimallyQualifiedFormat.GenericsOptions Or SymbolDisplayGenericsOptions.IncludeVariance) Private Overloads Shared Function Convert(parameter As ITypeParameterSymbol, semanticModel As SemanticModel, position As Integer, documentationCommentFormattingService As IDocumentationCommentFormattingService) As SignatureHelpSymbolParameter - Dim parts = New List(Of SymbolDisplayPart) + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() parts.AddRange(parameter.ToMinimalDisplayParts(semanticModel, position, s_minimallyQualifiedFormat)) AddConstraints(parameter, parts, semanticModel, position) @@ -152,13 +153,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parameter.Name, isOptional:=False, documentationFactory:=parameter.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), - displayParts:=parts) + displayParts:=parts.ToImmutableAndFree()) End Function - Private Shared Function AddConstraints(typeParam As ITypeParameterSymbol, - parts As List(Of SymbolDisplayPart), - semanticModel As SemanticModel, - position As Integer) As IList(Of SymbolDisplayPart) + Private Shared Sub AddConstraints( + typeParam As ITypeParameterSymbol, + parts As ArrayBuilder(Of SymbolDisplayPart), + semanticModel As SemanticModel, + position As Integer) Dim constraintTypes = typeParam.ConstraintTypes Dim constraintCount = TypeParameterSpecialConstraintCount(typeParam) + constraintTypes.Length @@ -203,9 +205,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parts.Add(Punctuation(SyntaxKind.CloseBraceToken)) End If End If - - Return parts - End Function + End Sub Private Shared Function TypeParameterSpecialConstraintCount(typeParam As ITypeParameterSymbol) As Integer Return If(typeParam.HasReferenceTypeConstraint, 1, 0) + diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.DelegateInvoke.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.DelegateInvoke.vb index bb409778cc4..3452cd42c43 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.DelegateInvoke.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.DelegateInvoke.vb @@ -2,10 +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. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -37,8 +39,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Return SpecializedCollections.SingletonEnumerable(item) End Function - Private Shared Function GetDelegateInvokePreambleParts(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim displayParts = New List(Of SymbolDisplayPart)() + Private Shared Function GetDelegateInvokePreambleParts(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim displayParts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() If invokeMethod.ContainingType.IsAnonymousType Then displayParts.Add(New SymbolDisplayPart(SymbolDisplayPartKind.MethodName, invokeMethod, invokeMethod.Name)) @@ -47,11 +49,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp End If displayParts.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return displayParts + Return displayParts.ToImmutableAndFree() End Function - Private Shared Function GetDelegateInvokeParameters(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer, documentationCommentFormattingService As IDocumentationCommentFormattingService, cancellationToken As CancellationToken) As IList(Of SignatureHelpSymbolParameter) - Dim parameters = New List(Of SignatureHelpSymbolParameter) + Private Shared Function GetDelegateInvokeParameters(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer, documentationCommentFormattingService As IDocumentationCommentFormattingService, cancellationToken As CancellationToken) As ImmutableArray(Of SignatureHelpSymbolParameter) + Dim parameters = ArrayBuilder(Of SignatureHelpSymbolParameter).GetInstance() For Each parameter In invokeMethod.Parameters cancellationToken.ThrowIfCancellationRequested() parameters.Add(New SignatureHelpSymbolParameter( @@ -61,13 +63,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp displayParts:=parameter.ToMinimalDisplayParts(semanticModel, position))) Next - Return parameters + Return parameters.ToImmutableAndFree() End Function - Private Shared Function GetDelegateInvokePostambleParts(invokeMethod As IMethodSymbol, - semanticModel As SemanticModel, - position As Integer) As IList(Of SymbolDisplayPart) - Dim parts = New List(Of SymbolDisplayPart) + Private Shared Function GetDelegateInvokePostambleParts( + invokeMethod As IMethodSymbol, + semanticModel As SemanticModel, + position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() parts.Add(Punctuation(SyntaxKind.CloseParenToken)) @@ -78,7 +81,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parts.AddRange(invokeMethod.ReturnType.ToMinimalDisplayParts(semanticModel, position)) End If - Return parts + Return parts.ToImmutableAndFree() End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.ElementAccess.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.ElementAccess.vb index 3fc0bbf04f9..e0a4a950218 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.ElementAccess.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.ElementAccess.vb @@ -2,10 +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. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -13,13 +15,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Partial Friend Class InvocationExpressionSignatureHelpProvider - Private Shared Function GetElementAccessItems(leftExpression As ExpressionSyntax, - semanticModel As SemanticModel, - structuralTypeDisplayService As IStructuralTypeDisplayService, - documentationCommentFormattingService As IDocumentationCommentFormattingService, - within As ISymbol, - defaultProperties As IList(Of IPropertySymbol), - cancellationToken As CancellationToken) As IEnumerable(Of SignatureHelpItem) + Private Shared Function GetElementAccessItems( + leftExpression As ExpressionSyntax, + semanticModel As SemanticModel, + structuralTypeDisplayService As IStructuralTypeDisplayService, + documentationCommentFormattingService As IDocumentationCommentFormattingService, + within As ISymbol, + defaultProperties As ImmutableArray(Of IPropertySymbol), + cancellationToken As CancellationToken) As IEnumerable(Of SignatureHelpItem) Dim throughType As ITypeSymbol = Nothing If leftExpression IsNot Nothing Then throughType = semanticModel.GetTypeInfo(leftExpression, cancellationToken).Type @@ -47,21 +50,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp GetIndexerPreambleParts(indexer, semanticModel, position), GetSeparatorParts(), GetIndexerPostambleParts(indexer, semanticModel, position), - indexer.Parameters.Select(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService)).ToList()) + indexer.Parameters.SelectAsArray(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService))) Return item End Function - Private Shared Function GetIndexerPreambleParts(symbol As IPropertySymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetIndexerPreambleParts(symbol As IPropertySymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() result.AddRange(symbol.ContainingType.ToMinimalDisplayParts(semanticModel, position)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetIndexerPostambleParts(symbol As IPropertySymbol, - semanticModel As SemanticModel, - position As Integer) As IList(Of SymbolDisplayPart) - Dim parts = New List(Of SymbolDisplayPart) + Private Shared Function GetIndexerPostambleParts( + symbol As IPropertySymbol, + semanticModel As SemanticModel, + position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() parts.Add(Punctuation(SyntaxKind.CloseParenToken)) Dim [property] = DirectCast(symbol, IPropertySymbol) @@ -71,8 +75,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parts.Add(Space()) parts.AddRange([property].Type.ToMinimalDisplayParts(semanticModel, position)) - Return parts + Return parts.ToImmutableAndFree() End Function - End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb index 4d223d56bf7..1d97feed906 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -101,18 +102,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim expressionType = If(typeInfo.Type, typeInfo.ConvertedType) Dim defaultProperties = If(expressionType Is Nothing, - SpecializedCollections.EmptyList(Of IPropertySymbol), - semanticModel.LookupSymbols(position, expressionType, includeReducedExtensionMethods:=True). - OfType(Of IPropertySymbol). - ToImmutableArrayOrEmpty(). - WhereAsArray(Function(p) p.IsIndexer). - FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols(options.HideAdvancedMembers, semanticModel.Compilation). - Sort(semanticModel, invocationExpression.SpanStart)) + ImmutableArray(Of IPropertySymbol).Empty, + semanticModel.LookupSymbols(position, expressionType, includeReducedExtensionMethods:=True). + OfType(Of IPropertySymbol). + ToImmutableArrayOrEmpty(). + WhereAsArray(Function(p) p.IsIndexer). + FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols(options.HideAdvancedMembers, semanticModel.Compilation). + Sort(semanticModel, invocationExpression.SpanStart)) Dim structuralTypeDisplayService = document.GetLanguageService(Of IStructuralTypeDisplayService)() Dim documentationCommentFormattingService = document.GetLanguageService(Of IDocumentationCommentFormattingService)() - Dim items = New List(Of SignatureHelpItem) + Dim items = ArrayBuilder(Of SignatureHelpItem).GetInstance() Dim accessibleMembers = ImmutableArray(Of ISymbol).Empty If memberGroup.Length > 0 Then accessibleMembers = GetAccessibleMembers(invocationExpression, semanticModel, within, memberGroup, cancellationToken) @@ -132,7 +133,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim selectedItem = TryGetSelectedIndex(accessibleMembers, symbolInfo.Symbol) Return CreateSignatureHelpItems( - items, textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken), + items.ToImmutableAndFree(), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken), selectedItem, parameterIndexOverride:=-1) End Function End Class diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.DelegateType.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.DelegateType.vb index 8936e239cec..dadbe719e48 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.DelegateType.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.DelegateType.vb @@ -2,21 +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. +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp - Partial Friend Class ObjectCreationExpressionSignatureHelpProvider - - Private Shared Function GetDelegateTypeConstructors(objectCreationExpression As ObjectCreationExpressionSyntax, - semanticModel As SemanticModel, - structuralTypeDisplayService As IStructuralTypeDisplayService, - documentationCommentFormattingService As IDocumentationCommentFormattingService, - delegateType As INamedTypeSymbol) As (items As IList(Of SignatureHelpItem), selectedItem As Integer?) + Private Shared Function GetDelegateTypeConstructors( + objectCreationExpression As ObjectCreationExpressionSyntax, + semanticModel As SemanticModel, + structuralTypeDisplayService As IStructuralTypeDisplayService, + documentationCommentFormattingService As IDocumentationCommentFormattingService, + delegateType As INamedTypeSymbol) As (items As ImmutableArray(Of SignatureHelpItem), selectedItem As Integer?) Dim invokeMethod = delegateType.DelegateInvokeMethod If invokeMethod Is Nothing Then Return (Nothing, Nothing) @@ -33,20 +34,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp suffixParts:=GetDelegateTypePostambleParts(), parameters:=GetDelegateTypeParameters(invokeMethod, semanticModel, position)) - Return (SpecializedCollections.SingletonList(item), 0) + Return (ImmutableArray.Create(item), 0) End Function - Private Shared Function GetDelegateTypePreambleParts(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetDelegateTypePreambleParts(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() result.AddRange(invokeMethod.ContainingType.ToMinimalDisplayParts(semanticModel, position)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetDelegateTypeParameters(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SignatureHelpSymbolParameter) + Private Shared Function GetDelegateTypeParameters(invokeMethod As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SignatureHelpSymbolParameter) Const TargetName As String = "target" - Dim parts = New List(Of SymbolDisplayPart)() + Dim parts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() If invokeMethod.ReturnsVoid Then parts.Add(Keyword(SyntaxKind.SubKeyword)) @@ -77,15 +78,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp parts.AddRange(invokeMethod.ReturnType.ToMinimalDisplayParts(semanticModel, position)) End If - Return {New SignatureHelpSymbolParameter( + Return ImmutableArray.Create(New SignatureHelpSymbolParameter( TargetName, isOptional:=False, documentationFactory:=Nothing, - displayParts:=parts)} + displayParts:=parts.ToImmutableAndFree())) End Function - Private Shared Function GetDelegateTypePostambleParts() As IList(Of SymbolDisplayPart) - Return {Punctuation(SyntaxKind.CloseParenToken)} + Private Shared Function GetDelegateTypePostambleParts() As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(Punctuation(SyntaxKind.CloseParenToken)) End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.NormalType.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.NormalType.vb index f2b58e7f002..c1be3d8a963 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.NormalType.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.NormalType.vb @@ -2,10 +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. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -20,7 +22,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp structuralTypeDisplayService As IStructuralTypeDisplayService, normalType As INamedTypeSymbol, within As ISymbol, - options As MemberDisplayOptions, cancellationToken As CancellationToken) As (items As IList(Of SignatureHelpItem), selectedItem As Integer?) + options As MemberDisplayOptions, cancellationToken As CancellationToken) As (items As ImmutableArray(Of SignatureHelpItem), selectedItem As Integer?) Dim accessibleConstructors = normalType.InstanceConstructors. WhereAsArray(Function(c) c.IsAccessibleWithin(within)). @@ -33,8 +35,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim documentationCommentFormattingService = document.GetLanguageService(Of IDocumentationCommentFormattingService)() - Dim items = accessibleConstructors.Select( - Function(c) ConvertNormalTypeConstructor(c, objectCreationExpression, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)).ToList() + Dim items = accessibleConstructors.SelectAsArray( + Function(c) ConvertNormalTypeConstructor(c, objectCreationExpression, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)) Dim currentConstructor = semanticModel.GetSymbolInfo(objectCreationExpression, cancellationToken) Dim selectedItem = TryGetSelectedIndex(accessibleConstructors, currentConstructor.Symbol) @@ -53,19 +55,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp constructor.GetDocumentationPartsFactory(semanticModel, position, documentationCommentFormattingService), GetNormalTypePreambleParts(constructor, semanticModel, position), GetSeparatorParts(), GetNormalTypePostambleParts(), - constructor.Parameters.Select(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService)).ToList()) + constructor.Parameters.SelectAsArray(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService))) Return item End Function - Private Shared Function GetNormalTypePreambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As IList(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Private Shared Function GetNormalTypePreambleParts(method As IMethodSymbol, semanticModel As SemanticModel, position As Integer) As ImmutableArray(Of SymbolDisplayPart) + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() result.AddRange(method.ContainingType.ToMinimalDisplayParts(semanticModel, position)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetNormalTypePostambleParts() As IList(Of SymbolDisplayPart) - Return {Punctuation(SyntaxKind.CloseParenToken)} + Private Shared Function GetNormalTypePostambleParts() As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(Punctuation(SyntaxKind.CloseParenToken)) End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/RaiseEventStatementSignatureHelpProvider.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/RaiseEventStatementSignatureHelpProvider.vb index acfbe505a74..d76944bac93 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/RaiseEventStatementSignatureHelpProvider.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/RaiseEventStatementSignatureHelpProvider.vb @@ -8,6 +8,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.DocumentationComments Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.LanguageService +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.SignatureHelp Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -93,7 +94,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim syntaxFacts = document.GetLanguageService(Of ISyntaxFactsService) Return CreateSignatureHelpItems( - allowedEvents.Select(Function(e) Convert(e, raiseEventStatement, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)).ToList(), + allowedEvents.SelectAsArray(Function(e) Convert(e, raiseEventStatement, semanticModel, structuralTypeDisplayService, documentationCommentFormattingService)), textSpan, GetCurrentArgumentState(root, position, syntaxFacts, textSpan, cancellationToken), selectedItemIndex:=Nothing, parameterIndexOverride:=-1) End Function @@ -117,18 +118,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp GetPreambleParts(eventSymbol, semanticModel, position), GetSeparatorParts(), GetPostambleParts(), - type.DelegateInvokeMethod.GetParameters().Select(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService)).ToList()) + type.DelegateInvokeMethod.GetParameters().SelectAsArray(Function(p) Convert(p, semanticModel, position, documentationCommentFormattingService))) Return item End Function Private Shared Function GetPreambleParts( - eventSymbol As IEventSymbol, - semanticModel As SemanticModel, - position As Integer - ) As IList(Of SymbolDisplayPart) + eventSymbol As IEventSymbol, + semanticModel As SemanticModel, + position As Integer) As ImmutableArray(Of SymbolDisplayPart) - Dim result = New List(Of SymbolDisplayPart)() + Dim result = ArrayBuilder(Of SymbolDisplayPart).GetInstance() result.AddRange(eventSymbol.ContainingType.ToMinimalDisplayParts(semanticModel, position)) result.Add(Punctuation(SyntaxKind.DotToken)) @@ -140,11 +140,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp result.AddRange(eventSymbol.ToMinimalDisplayParts(semanticModel, position, format)) result.Add(Punctuation(SyntaxKind.OpenParenToken)) - Return result + Return result.ToImmutableAndFree() End Function - Private Shared Function GetPostambleParts() As IList(Of SymbolDisplayPart) - Return {Punctuation(SyntaxKind.CloseParenToken)} + Private Shared Function GetPostambleParts() As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(Punctuation(SyntaxKind.CloseParenToken)) End Function End Class End Namespace diff --git a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/SignatureHelpUtilities.vb b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/SignatureHelpUtilities.vb index 816a7fcb4e0..584acf78a48 100644 --- a/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/SignatureHelpUtilities.vb +++ b/src/roslyn/src/Features/VisualBasic/Portable/SignatureHelp/SignatureHelpUtilities.vb @@ -7,7 +7,6 @@ Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp - Friend Module SignatureHelpUtilities Private ReadOnly s_getArgumentListOpenToken As Func(Of ArgumentListSyntax, SyntaxToken) = Function(list) list.OpenParenToken Private ReadOnly s_getTypeArgumentListOpenToken As Func(Of TypeArgumentListSyntax, SyntaxToken) = Function(list) list.OpenParenToken diff --git a/src/roslyn/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/roslyn/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index 6d534424425..60324fc815a 100644 --- a/src/roslyn/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/roslyn/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -1726,6 +1726,8 @@ End Class Dim srcA2 = ReloadableAttributeSrc Dim srcB2 = " +Imports System.Runtime.CompilerServices + Class C Sub F() diff --git a/src/roslyn/src/Features/VisualBasicTest/InvertIf/InvertIfDirectiveTests.vb b/src/roslyn/src/Features/VisualBasicTest/InvertIf/InvertIfDirectiveTests.vb new file mode 100644 index 00000000000..48c5a8956ee --- /dev/null +++ b/src/roslyn/src/Features/VisualBasicTest/InvertIf/InvertIfDirectiveTests.vb @@ -0,0 +1,199 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeRefactoringVerifier(Of + Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertSingleLineIfCodeRefactoringProvider) + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InvertIf + + Public NotInheritable Class InvertIfDirectiveTests + Private Shared Async Function TestAsync(testCode As String, fixedCode As String) As Task + Await New VerifyVB.Test With { + .TestCode = testCode, + .FixedCode = fixedCode + }.RunAsync() + End Function + + + Public Async Function TestIfDirective1() As Task + Await TestAsync(" + [||]#if true + #else + #end if + ", " + #if False + #else + #end if + ") + End Function + + + Public Async Function TestIfDirective2() As Task + Await TestAsync(" + [||]#if true + #else + + #end if + ", " + #if False + + #else + #end if + ") + End Function + + + Public Async Function TestIfDirective3() As Task + Await TestAsync(" + [||]#if true + + #else + #end if + ", " + #if False + #else + + #end if + ") + End Function + + + Public Async Function TestIfDirective4() As Task + Await TestAsync(" + [||]#if true + class C + end class + #else + interface I + + end interface + #end if + ", " + #if False + interface I + + end interface + #else + class C + end class + #end if + ") + End Function + + + Public Async Function TestIfDirective5() As Task + Await TestAsync(" + [||]#if Not true + class C + end class + #else + interface I + + end interface + #end if + ", " + #if true + interface I + + end interface + #else + class C + end class + #end if + ") + End Function + + + Public Async Function TestIfDirective6() As Task + Await TestAsync(" + [||]#if NAME + class C + end class + #else + interface I + + end interface + #end if + ", " + #if Not NAME + interface I + + end interface + #else + class C + end class + #end if + ") + End Function + + + Public Async Function TestIfDirective7() As Task + Await TestAsync(" + [||]#if A andalso B + class C + end class + #else + interface I + + end interface + #end if + ", " + #if Not (A andalso B) + interface I + + end interface + #else + class C + end class + #end if + ") + End Function + + + Public Async Function TestIfDirective8() As Task + Await TestAsync(" + [||]#if (true) + class C + end class + #else + interface I + + end interface + #end if + ", " + #if (False) + interface I + + end interface + #else + class C + end class + #end if + ") + End Function + + + Public Async Function TestIfDirective9() As Task + Await TestAsync(" + [||]#if (true) + class C + end class + #else + interface I + + end interface + #end if + ", " + #if (False) + interface I + + end interface + #else + class C + end class + #end if + ") + End Function + End Class +End Namespace diff --git a/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs b/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs index 7a1cce089f8..d2e7cc11cf3 100644 --- a/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs +++ b/src/roslyn/src/LanguageServer/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs @@ -29,7 +29,7 @@ internal sealed class VisualDiagnosticsServiceFactory( LspWorkspaceRegistrationService lspWorkspaceRegistrationService) : ILspServiceFactory, IOnServiceBrokerInitialized { private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; - private readonly Lazy _OnInitializedService = new Lazy(() => new OnInitializedService(lspWorkspaceRegistrationService)); + private readonly Lazy _OnInitializedService = new(() => new OnInitializedService(lspWorkspaceRegistrationService)); public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { @@ -46,7 +46,7 @@ private class OnInitializedService : ILspService, IOnInitialized, IOnServiceBrok private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; private IVisualDiagnosticsLanguageService? _visualDiagnosticsLanguageService; private CancellationToken _cancellationToken; - private static readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); + private static readonly TaskCompletionSource _taskCompletionSource = new(); public OnInitializedService(LspWorkspaceRegistrationService lspWorkspaceRegistrationService) { diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 64fa762c9d5..6d0590087b5 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; public sealed class LspFileChangeWatcherTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerHostTests(testOutputHelper) { - private readonly ClientCapabilities _clientCapabilitiesWithFileWatcherSupport = new ClientCapabilities + private readonly ClientCapabilities _clientCapabilitiesWithFileWatcherSupport = new() { Workspace = new WorkspaceClientCapabilities { diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs index 5d4dc274392..1216d9d3039 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs @@ -31,7 +31,7 @@ internal sealed class ServiceBrokerFactory private readonly ExportProvider _exportProvider; private readonly WrappedServiceBroker _wrappedServiceBroker; private Task _bridgeCompletionTask; - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly ImmutableArray _onServiceBrokerInitialized; [ImportingConstructor] diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs index 28347ea9e1e..4c65e7361be 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs @@ -16,7 +16,7 @@ internal sealed class BrokeredServiceBridgeManifest : IBrokeredServiceBridgeMani { internal const string MonikerName = "Microsoft.VisualStudio.Server.IBrokeredServiceBridgeManifest"; internal const string MonikerVersion = "0.1"; - private static readonly ServiceMoniker s_serviceMoniker = new ServiceMoniker(MonikerName, new Version(MonikerVersion)); + private static readonly ServiceMoniker s_serviceMoniker = new(MonikerName, new Version(MonikerVersion)); private static readonly ServiceRpcDescriptor s_serviceDescriptor = new ServiceJsonRpcDescriptor( s_serviceMoniker, ServiceJsonRpcDescriptor.Formatters.UTF8, diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/FileBasedProgramsProjectSystem.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/FileBasedProgramsProjectSystem.cs index bc6c4688ad3..f5f422c5563 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/FileBasedProgramsProjectSystem.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/FileBasedProgramsProjectSystem.cs @@ -127,7 +127,6 @@ public async ValueTask TryRemoveMiscellaneousDocumentAsync(DocumentUri uri) foreach (var diagnostic in diagnostics) { - // https://github.com/dotnet/roslyn/issues/78688: Surface diagnostics in editor _logger.LogError($"{diagnostic.Location.Path}{diagnostic.Location.Span.Start}: {diagnostic.Message}"); } diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/VirtualProjectXmlProvider.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/VirtualProjectXmlProvider.cs index a5fe8cd5606..15147930f12 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/VirtualProjectXmlProvider.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/FileBasedPrograms/VirtualProjectXmlProvider.cs @@ -203,13 +203,13 @@ internal static string MakeVirtualProjectContent_DirectFallback(string documentF Contract.ThrowIfFalse(PathUtilities.IsAbsolute(documentFilePath)); var artifactsPath = GetArtifactsPath(documentFilePath); - var targetFramework = Environment.GetEnvironmentVariable("DOTNET_RUN_FILE_TFM") ?? "net10.0"; + var targetFramework = Environment.GetEnvironmentVariable("DOTNET_RUN_FILE_TFM") ?? "net$(BundledNETCoreAppTargetFrameworkVersion)"; var virtualProjectXml = $""" - false - {SecurityElement.Escape(artifactsPath)} + {SecurityElement.Escape(artifactsPath)}\obj\ + {SecurityElement.Escape(artifactsPath)}\bin\ diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs index 251225e0572..24f2b0fbd2b 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs @@ -58,13 +58,13 @@ private sealed class FileChangeContext : IFileChangeContext /// A lock to guard updates to . Using a reader/writer lock since file change notifications can be pretty chatty /// and so we want to be able to process changes as fast as possible. /// - private readonly ReaderWriterLockSlim _watchedFilesLock = new ReaderWriterLockSlim(); + private readonly ReaderWriterLockSlim _watchedFilesLock = new(); /// /// The list of file paths we're watching manually that were outside the directories being watched. The count in this case counts /// the number of /// - private readonly Dictionary _watchedFiles = new Dictionary(s_stringComparer); + private readonly Dictionary _watchedFiles = new(s_stringComparer); private static readonly StringComparer s_stringComparer = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; private static readonly StringComparison s_stringComparison = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs index da5f2c62840..8c53173c788 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectLoader.cs @@ -362,7 +362,8 @@ private async Task ReloadProjectAsync(ProjectToLoad projectToLoad, ToastEr _projectSystemHostInfo); var loadedProject = new LoadedProject(projectSystemProject, projectFactory, _fileChangeWatcher, _targetFrameworkManager); - loadedProject.NeedsReload += (_, _) => _projectsToReload.AddWork(projectToLoad with { ReportTelemetry = false }); + loadedProject.NeedsReload += (_, _) => + _projectsToReload.AddWork(projectToLoad with { ReportTelemetry = false }); return (loadedProject, alreadyExists: false); } diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs index 7dcc9c22864..86751f83c7b 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry; using Microsoft.CodeAnalysis.MSBuild; using Microsoft.CodeAnalysis.ProjectSystem; +using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.Extensions.FileSystemGlobbing; using Microsoft.Extensions.Logging; @@ -29,6 +30,7 @@ internal sealed class LoadedProject : IDisposable private readonly ProjectSystemProjectOptionsProcessor _optionsProcessor; private readonly IFileChangeContext _sourceFileChangeContext; private readonly IFileChangeContext _projectFileChangeContext; + private readonly IFileChangeContext _assetsFileChangeContext; private readonly ProjectTargetFrameworkManager _targetFrameworkManager; /// @@ -40,6 +42,7 @@ internal sealed class LoadedProject : IDisposable /// private Lazy>? _mostRecentFileMatchers; private IWatchedFile? _mostRecentProjectAssetsFileWatcher; + private Checksum _mostRecentProjectAssetsFileChecksum; private ImmutableArray _mostRecentMetadataReferences = []; private ImmutableArray _mostRecentAnalyzerReferences = []; @@ -63,6 +66,9 @@ public LoadedProject(ProjectSystemProject projectSystemProject, ProjectSystemPro _projectFileChangeContext = fileWatcher.CreateContext([]); _projectFileChangeContext.FileChanged += ProjectFileChangeContext_FileChanged; _projectFileChangeContext.EnqueueWatchingFile(_projectFilePath); + + _assetsFileChangeContext = fileWatcher.CreateContext([]); + _assetsFileChangeContext.FileChanged += AssetsFileChangeContext_FileChanged; } private void SourceFileChangeContext_FileChanged(object? sender, string filePath) @@ -95,6 +101,23 @@ private void ProjectFileChangeContext_FileChanged(object? sender, string filePat NeedsReload?.Invoke(this, EventArgs.Empty); } + private void AssetsFileChangeContext_FileChanged(object? sender, string filePath) + { + Shared.Utilities.IOUtilities.PerformIO(() => + { + // We only want to trigger design time build if the assets file content actually changed from the last time this handler was called. + // Sometimes we can get a change event where no content changed (e.g. for a failed restore). + // In such cases, proceeding with design-time build can put us in a restore loop (since the design-time build notices that assets are missing). + using var assetsFileStream = File.OpenRead(filePath); + var checksum = Checksum.Create(assetsFileStream); + if (_mostRecentProjectAssetsFileChecksum != checksum) + { + _mostRecentProjectAssetsFileChecksum = checksum; + NeedsReload?.Invoke(this, EventArgs.Empty); + } + }); + } + public event EventHandler? NeedsReload; public string? GetTargetFramework() @@ -282,8 +305,9 @@ void WatchProjectAssetsFile(ProjectFileInfo currentProjectInfo) // Dispose of the last once since we're changing the file we're watching. _mostRecentProjectAssetsFileWatcher?.Dispose(); _mostRecentProjectAssetsFileWatcher = currentProjectInfo.ProjectAssetsFilePath is { } assetsFilePath - ? _projectFileChangeContext.EnqueueWatchingFile(assetsFilePath) + ? _assetsFileChangeContext.EnqueueWatchingFile(assetsFilePath) : null; + _mostRecentProjectAssetsFileChecksum = default; } } diff --git a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs index d535fbfade1..8c65c4b7e3b 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Services/StarredCompletions/StarredCompletionsAssemblyHelper.cs @@ -29,7 +29,7 @@ internal static class StarredCompletionAssemblyHelper /// A gate to guard the actual creation of . This just prevents us from trying to create the provider more than once; once the field is set it /// won't change again. /// - private static readonly SemaphoreSlim s_gate = new SemaphoreSlim(initialCount: 1); + private static readonly SemaphoreSlim s_gate = new(initialCount: 1); private static bool s_previousCreationFailed = false; private static CompletionProvider? s_completionProvider; diff --git a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs index 864b196093d..37b16012b5a 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs @@ -73,8 +73,8 @@ public Task ShutdownAsync(string message = "Shutting down") } } - private readonly TaskCompletionSource _shuttingDown = new TaskCompletionSource(); - private readonly TaskCompletionSource _exiting = new TaskCompletionSource(); + private readonly TaskCompletionSource _shuttingDown = new(); + private readonly TaskCompletionSource _exiting = new(); protected override ILspServices ConstructLspServices() { diff --git a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index f8f0cb307fb..61b42df61ac 100644 --- a/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/roslyn/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -290,7 +290,7 @@ private async Task ProcessQueueAsync() var message = $"Error occurred processing queue: {ex.Message}."; if (lspServices is not null) { - await _languageServer.ShutdownAsync("Error processing queue, shutting down").ConfigureAwait(false); + await _languageServer.ShutdownAsync(message).ConfigureAwait(false); await _languageServer.ExitAsync().ConfigureAwait(false); } diff --git a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs index 900a1dd8919..89d33544a70 100644 --- a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs +++ b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs @@ -42,7 +42,7 @@ private async Task> GetDiagnosticsAsync( { var text = await document.GetTextAsync().ConfigureAwait(false); var dxs = await _diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project, [document.Id], diagnosticIds: null, shouldIncludeAnalyzer: null, + project, [document.Id], diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); dxs = dxs.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); documentDiagnostics = await CodeAnalysis.Diagnostics.Extensions.ToDiagnosticsAsync( @@ -56,7 +56,7 @@ filterSpan is null if (getProjectDiagnostics) { var dxs = await _diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: null, shouldIncludeAnalyzer: null, + project, documentIds: default, diagnosticIds: null, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, CancellationToken.None); dxs = dxs.WhereAsArray(d => _includeSuppressedDiagnostics || !d.IsSuppressed); projectDiagnostics = await CodeAnalysis.Diagnostics.Extensions.ToDiagnosticsAsync(dxs.Where(d => d.DocumentId is null), project, CancellationToken.None); diff --git a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 9cad08424bb..ad45e29418c 100644 --- a/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/roslyn/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -62,12 +62,12 @@ private sealed class TestSpanMapperProvider : IDocumentServiceProvider internal sealed class TestSpanMapper : ISpanMappingService { - private static readonly LinePositionSpan s_mappedLinePosition = new LinePositionSpan(new LinePosition(0, 0), new LinePosition(0, 5)); + private static readonly LinePositionSpan s_mappedLinePosition = new(new LinePosition(0, 0), new LinePosition(0, 5)); private static readonly string s_mappedFilePath = "c:\\MappedFile_\ue25b\ud86d\udeac.cs"; internal static readonly string GeneratedFileName = "GeneratedFile_\ue25b\ud86d\udeac.cs"; - internal static readonly LSP.Location MappedFileLocation = new LSP.Location + internal static readonly LSP.Location MappedFileLocation = new() { Range = ProtocolConversions.LinePositionToRange(s_mappedLinePosition), DocumentUri = ProtocolConversions.CreateAbsoluteDocumentUri(s_mappedFilePath) @@ -208,14 +208,14 @@ private protected static LSP.TextDocumentIdentifier CreateTextDocumentIdentifier } private protected static LSP.TextDocumentPositionParams CreateTextDocumentPositionParams(LSP.Location caret, ProjectId? projectContext = null) - => new LSP.TextDocumentPositionParams() + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri, projectContext), Position = caret.Range.Start }; private protected static LSP.MarkupContent CreateMarkupContent(LSP.MarkupKind kind, string value) - => new LSP.MarkupContent() + => new() { Kind = kind, Value = value @@ -226,7 +226,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( LSP.VSInternalCompletionInvokeKind invokeKind, string triggerCharacter, LSP.CompletionTriggerKind triggerKind) - => new LSP.CompletionParams() + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), Position = caret.Range.Start, @@ -284,7 +284,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( } private protected static LSP.TextEdit GenerateTextEdit(string newText, int startLine, int startChar, int endLine, int endChar) - => new LSP.TextEdit + => new() { NewText = newText, Range = new LSP.Range @@ -359,9 +359,39 @@ private protected async Task CreateXmlTestLspServerAsync( return await TestLspServer.CreateAsync(workspace, lspOptions, TestOutputLspLogger); } + private void CheckForCompositionErrors(TestComposition composition) + { + // The test compositions tend to have a bunch of errors. + // We only want to fail the test if we're seeing errors in relevant parts to the language server. + // This isn't foolproof, but helps catch issues early. + + var config = composition.GetCompositionConfiguration(); + var hasLanguageServerErrors = config.CompositionErrors.Flatten().Any(error => error.Parts.Any(IsRelevantPartError)); + + if (hasLanguageServerErrors) + { + try + { + config.ThrowOnErrors(); + } + catch (CompositionFailedException ex) + { + // The ToString for the composition failed exception doesn't output a nice set of errors by default, so log it separately + this.TestOutputLspLogger.LogError($"Encountered errors in the MEF composition: {ex.Message}{Environment.NewLine}{ex.ErrorsAsString}"); + throw; + } + } + + bool IsRelevantPartError(ComposedPart part) + { + return part.Definition.Type.FullName?.Contains("Microsoft.CodeAnalysis.LanguageServer") == true; + } + } + internal async Task CreateWorkspaceAsync( InitializationOptions? options, string? workspaceKind, bool mutatingLspWorkspace, TestComposition? composition = null) { + CheckForCompositionErrors(composition ?? Composition); var workspace = new LspTestWorkspace( composition?.ExportProviderFactory.CreateExportProvider() ?? await CreateExportProviderAsync(), workspaceKind, @@ -494,7 +524,8 @@ private protected static LSP.Location GetLocationPlusOne(LSP.Location originalLo private static LSP.DidChangeTextDocumentParams CreateDidChangeTextDocumentParams( DocumentUri documentUri, - ImmutableArray<(LSP.Range Range, string Text)> changes) + ImmutableArray<(LSP.Range Range, string Text)> changes, + int version = 0) { var changeEvents = changes.Select(change => new LSP.TextDocumentContentChangeEvent { @@ -506,25 +537,27 @@ private static LSP.DidChangeTextDocumentParams CreateDidChangeTextDocumentParams { TextDocument = new LSP.VersionedTextDocumentIdentifier { - DocumentUri = documentUri + DocumentUri = documentUri, + Version = version }, ContentChanges = changeEvents }; } - private static LSP.DidOpenTextDocumentParams CreateDidOpenTextDocumentParams(DocumentUri uri, string source, string languageId = "") - => new LSP.DidOpenTextDocumentParams + private static LSP.DidOpenTextDocumentParams CreateDidOpenTextDocumentParams(DocumentUri uri, string source, string languageId = "", int version = 0) + => new() { TextDocument = new LSP.TextDocumentItem { Text = source, DocumentUri = uri, - LanguageId = languageId + LanguageId = languageId, + Version = version } }; private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(DocumentUri uri) - => new LSP.DidCloseTextDocumentParams() + => new() { TextDocument = new LSP.TextDocumentIdentifier { @@ -704,7 +737,7 @@ public Task ExecutePreSerializedRequestAsync(string methodName, JsonDocument ser return _clientRpc.InvokeWithParameterObjectAsync(methodName, serializedRequest); } - public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null, string languageId = "") + public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null, string languageId = "", int version = 0) { if (text == null) { @@ -714,13 +747,13 @@ public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null text = sourceText.ToString(); } - var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString(), languageId); + var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString(), languageId, version); await ExecuteRequestAsync(LSP.Methods.TextDocumentDidOpenName, didOpenParams, CancellationToken.None); } /// /// Opens a document in the workspace only, and waits for workspace operations. - /// Use if the document should be opened in LSP"/> + /// Use if the document should be opened in LSP"/> /// public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openAllLinkedDocuments, SourceText? text = null) { @@ -745,23 +778,34 @@ public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openA await WaitForWorkspaceOperationsAsync(TestWorkspace); } - public Task ReplaceTextAsync(DocumentUri documentUri, params (LSP.Range Range, string Text)[] changes) + public Task ReplaceTextAsync(DocumentUri documentUri, int version, params (LSP.Range Range, string Text)[] changes) { var didChangeParams = CreateDidChangeTextDocumentParams( documentUri, - [.. changes]); + [.. changes], + version); return ExecuteRequestAsync(LSP.Methods.TextDocumentDidChangeName, didChangeParams, CancellationToken.None); } - public Task InsertTextAsync(DocumentUri documentUri, params (int Line, int Column, string Text)[] changes) + public Task ReplaceTextAsync(DocumentUri documentUri, params (LSP.Range Range, string Text)[] changes) { - return ReplaceTextAsync(documentUri, [.. changes.Select(change => (new LSP.Range + return ReplaceTextAsync(documentUri, version: 0, changes); + } + + public Task InsertTextAsync(DocumentUri documentUri, int version, params (int Line, int Column, string Text)[] changes) + { + return ReplaceTextAsync(documentUri, version, [.. changes.Select(change => (new LSP.Range { Start = new LSP.Position { Line = change.Line, Character = change.Column }, End = new LSP.Position { Line = change.Line, Character = change.Column } }, change.Text))]); } + public Task InsertTextAsync(DocumentUri documentUri, params (int Line, int Column, string Text)[] changes) + { + return InsertTextAsync(documentUri, version: 0, changes); + } + public Task DeleteTextAsync(DocumentUri documentUri, params (int StartLine, int StartColumn, int EndLine, int EndColumn)[] changes) { return ReplaceTextAsync(documentUri, [.. changes.Select(change => (new LSP.Range diff --git a/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs b/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs index b5c55b447be..f4fa2a79b2b 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Extensions/Extensions.cs @@ -260,7 +260,7 @@ public static string GetMarkdownLanguageName(this Document document) } public static ClassifiedTextElement GetClassifiedText(this DefinitionItem definition) - => new ClassifiedTextElement(definition.DisplayParts.Select(part => new ClassifiedTextRun(part.Tag.ToClassificationTypeName(), part.Text))); + => new(definition.DisplayParts.Select(part => new ClassifiedTextRun(part.Tag.ToClassificationTypeName(), part.Text))); private static bool TryGetVSCompletionListSetting(ClientCapabilities clientCapabilities, [NotNullWhen(returnValue: true)] out VSInternalCompletionListSetting? completionListSetting) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 2c8495ffc55..5fcf94c969c 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -315,6 +315,20 @@ public static TextSpan RangeToTextSpan(LSP.Range range, SourceText text) { var linePositionSpan = RangeToLinePositionSpan(range); + // Handle the specific case where the end position is exactly one line beyond the document bounds + // and the end character is 0 (start of the non-existent next line). + // This can happen when deleting the last line, where LSP clients are allowed (by the spec) to + // send an end position referencing the start of the next line (which doesn't exist). + if (text.Lines.Count > 0 && + linePositionSpan.End.Line == text.Lines.Count && + linePositionSpan.End.Character == 0) + { + // Clamp the end position to the end of the last line + var lastLine = text.Lines[text.Lines.Count - 1]; + var clampedEnd = new LinePosition(text.Lines.Count - 1, lastLine.End - lastLine.Start); + linePositionSpan = new LinePositionSpan(linePositionSpan.Start, clampedEnd); + } + try { try @@ -351,16 +365,16 @@ public static LSP.TextEdit TextChangeToTextEdit(TextChange textChange, SourceTex } public static TextChange TextEditToTextChange(LSP.TextEdit edit, SourceText oldText) - => new TextChange(RangeToTextSpan(edit.Range, oldText), edit.NewText); + => new(RangeToTextSpan(edit.Range, oldText), edit.NewText); public static TextChange ContentChangeEventToTextChange(LSP.TextDocumentContentChangeEvent changeEvent, SourceText text) - => new TextChange(RangeToTextSpan(changeEvent.Range, text), changeEvent.Text); + => new(RangeToTextSpan(changeEvent.Range, text), changeEvent.Text); public static LSP.Position LinePositionToPosition(LinePosition linePosition) - => new LSP.Position { Line = linePosition.Line, Character = linePosition.Character }; + => new() { Line = linePosition.Line, Character = linePosition.Character }; public static LSP.Range LinePositionToRange(LinePositionSpan linePositionSpan) - => new LSP.Range { Start = LinePositionToPosition(linePositionSpan.Start), End = LinePositionToPosition(linePositionSpan.End) }; + => new() { Start = LinePositionToPosition(linePositionSpan.Start), End = LinePositionToPosition(linePositionSpan.End) }; public static LSP.Range TextSpanToRange(TextSpan textSpan, SourceText text) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 3b9d7f71048..ef52d36a238 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -209,11 +209,12 @@ private async Task ApplyCodeFixesForSpecificDiagnosticIdAsync( var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var range = new TextSpan(0, tree.Length); - // Compute diagnostics for everything that is not an IDE analyzer var diagnosticService = document.Project.Solution.Services.GetRequiredService(); - var diagnostics = await diagnosticService.GetDiagnosticsForSpanAsync(document, range, - shouldIncludeDiagnostic: static diagnosticId => !(IDEDiagnosticIdToOptionMappingHelper.IsKnownIDEDiagnosticId(diagnosticId)), - priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + var diagnostics = await diagnosticService.GetDiagnosticsForSpanAsync( + document, range, + // Compute diagnostics for everything that is *NOT* an IDE analyzer + DiagnosticIdFilter.Exclude(IDEDiagnosticIdToOptionMappingHelper.KnownIDEDiagnosticIds), + priority: null, DiagnosticKind.All, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs index 05b65aa3247..0b81fa56c4d 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Options/LanguageServerProjectSystemOptionsStorage.cs @@ -13,15 +13,15 @@ internal static class LanguageServerProjectSystemOptionsStorage /// /// A folder to log binlogs to when running design-time builds. /// - public static readonly Option2 BinaryLogPath = new Option2("dotnet_binary_log_path", defaultValue: null, s_optionGroup); + public static readonly Option2 BinaryLogPath = new("dotnet_binary_log_path", defaultValue: null, s_optionGroup); /// /// Whether or not automatic nuget restore is enabled. /// - public static readonly Option2 EnableAutomaticRestore = new Option2("dotnet_enable_automatic_restore", defaultValue: true, s_optionGroup); + public static readonly Option2 EnableAutomaticRestore = new("dotnet_enable_automatic_restore", defaultValue: true, s_optionGroup); /// /// Whether to use the new 'dotnet run app.cs' (file-based programs) experience. /// - public static readonly Option2 EnableFileBasedPrograms = new Option2("dotnet_enable_file_based_programs", defaultValue: true, s_optionGroup); + public static readonly Option2 EnableFileBasedPrograms = new("dotnet_enable_file_based_programs", defaultValue: true, s_optionGroup); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs new file mode 100644 index 00000000000..0d82c966061 --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedAction.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Microsoft.CodeAnalysis.CodeRefactorings; + +namespace Microsoft.CodeAnalysis.Suggestions; + +/// +/// Similar to SuggestedAction, but in a location that can be used by +/// both local Roslyn and LSP. +/// +internal sealed class SuggestedAction +{ + /// + /// Original provider that created this suggested action. This is only used for extension exception management. If + /// we encounter a (non-cancellation) exception thrown when using this provider, we wil disable it for the rest of + /// the session. + /// + public object Provider { get; } + + /// + /// Underlying responsible for making the desired change to the user's code. + /// + public CodeAction CodeAction { get; } + + /// + /// Priority that this action should be presented with. Higher priority actions should be presented more + /// prominently to the user. + /// + internal CodeActionPriority CodeActionPriority { get; } + + /// + /// If this is a code refactoring, what sort of code refactoring it is. Used to present different sorts of UI + /// affordances in certain hosts. Can be if this was not created by a code refactoring but + /// was instead created by a code fix. + /// + public CodeRefactoringKind? CodeRefactoringKind { get; } + + /// + /// If this was created to fix specific diagnostics, these are those diagnostics. This may be empty if the action + /// represents a code refactoring and not a code fix. + /// + public ImmutableArray Diagnostics { get; } + + /// + /// Optional flavors for this action. Flavors are child actions that are presented as simple links, not as + /// menu-items. For example the flavors to 'fix all in document/project/solution'. It present, will be empty and will be . + /// + public SuggestedActionFlavors? Flavors { get; } + + /// + /// Nested actions that should ideally be shown in a sub-menu under this item. This action will not itself be + /// invocable, and serves only as a named container for these sub-actions. If this is non-empty, then and will be . + /// + public ImmutableArray NestedActionSets { get; } + + /// + /// Non-null if this is a fix-all or refactor-all action. If this is non-null, then will be + /// and will be empty. + /// + public IRefactorOrFixAllState? RefactorOrFixAllState { get; } + + private SuggestedAction( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + object provider, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + SuggestedActionFlavors? flavors, + ImmutableArray nestedActionSets, + IRefactorOrFixAllState? refactorOrFixAllState) + { + Provider = provider; + CodeAction = codeAction; + CodeActionPriority = codeActionPriority; + CodeRefactoringKind = codeRefactoringKind; + Diagnostics = diagnostics; + Flavors = flavors; + NestedActionSets = nestedActionSets; + RefactorOrFixAllState = refactorOrFixAllState; + } + + public static SuggestedAction CreateWithFlavors( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + object provider, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + SuggestedActionFlavors? flavors) + { + return new(codeAction, codeActionPriority, provider, codeRefactoringKind, diagnostics, flavors, nestedActionSets: [], refactorOrFixAllState: null); + } + + public static SuggestedAction CreateWithNestedActionSets( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + object provider, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + ImmutableArray nestedActionSets) + { + Contract.ThrowIfTrue(nestedActionSets.IsDefaultOrEmpty); + return new(codeAction, codeActionPriority, provider, codeRefactoringKind, diagnostics, flavors: null, nestedActionSets, refactorOrFixAllState: null); + } + + public static SuggestedAction CreateRefactorOrFixAll( + CodeAction codeAction, + CodeActionPriority codeActionPriority, + CodeRefactoringKind? codeRefactoringKind, + ImmutableArray diagnostics, + IRefactorOrFixAllState refactorOrFixAllState) + { + Contract.ThrowIfNull(refactorOrFixAllState); + return new(codeAction, codeActionPriority, refactorOrFixAllState.Provider, codeRefactoringKind, diagnostics, flavors: null, nestedActionSets: [], refactorOrFixAllState); + } +} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs new file mode 100644 index 00000000000..ad4ecc4987d --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionFlavors.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.Suggestions; + +internal readonly record struct SuggestedActionFlavors( + string Title, + ImmutableArray Actions); diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.cs new file mode 100644 index 00000000000..c0178808558 --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/SuggestedActionSet.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.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Suggestions; + +/// +/// Similar to SuggestedActionSet, but in a location that can be used +/// by both local Roslyn and LSP. +/// +internal sealed class SuggestedActionSet( + string? categoryName, + ImmutableArray actions, + string? title, + CodeActionPriority priority, + TextSpan? applicableToSpan) +{ + public readonly string? CategoryName = categoryName; + public readonly ImmutableArray Actions = actions; + public readonly string? Title = title; + public readonly CodeActionPriority Priority = priority; + public readonly TextSpan? ApplicableToSpan = applicableToSpan; +} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs similarity index 92% rename from src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs rename to src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs index d3c4b353816..8aa47eedef7 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedPredefinedSuggestedActionCategoryNames.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedPredefinedSuggestedActionCategoryNames.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. -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; +namespace Microsoft.CodeAnalysis.Suggestions; /// /// Equivalent to PredefinedSuggestedActionCategoryNames, but in a location that diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSetComparer.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionSetComparer.cs similarity index 92% rename from src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSetComparer.cs rename to src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionSetComparer.cs index 2c0ac6a6313..b1bce2f8af0 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSetComparer.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionSetComparer.cs @@ -8,9 +8,9 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; +namespace Microsoft.CodeAnalysis.Suggestions; -internal sealed class UnifiedSuggestedActionSetComparer : IComparer +internal sealed class UnifiedSuggestedActionSetComparer : IComparer { private readonly TextSpan? _targetSpan; @@ -49,7 +49,7 @@ private static int Distance(TextSpan? maybeA, TextSpan? maybeB) return startsDistance + endsDistance; } - public int Compare(UnifiedSuggestedActionSet x, UnifiedSuggestedActionSet y) + public int Compare(SuggestedActionSet x, SuggestedActionSet y) { if (!_targetSpan.HasValue || !x.ApplicableToSpan.HasValue || !y.ApplicableToSpan.HasValue) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionsSource.cs similarity index 65% rename from src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs rename to src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionsSource.cs index 77219716714..8ca18bc14a8 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Features/Suggestions/UnifiedSuggestedActionsSource.cs @@ -13,47 +13,46 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Diagnostics; 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; -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; +namespace Microsoft.CodeAnalysis.Suggestions; + +using CodeFixGroupKey = (DiagnosticData diagnostic, CodeActionPriority firstPriority, CodeActionPriority? secondPriority); /// /// Provides mutual code action logic for both local and LSP scenarios -/// via intermediate interface . +/// via intermediate interface . /// internal sealed class UnifiedSuggestedActionsSource { /// /// Gets, filters, and orders code fixes. /// - public static async ValueTask> GetFilterAndOrderCodeFixesAsync( - Workspace workspace, + public static async ValueTask> GetFilterAndOrderCodeFixesAsync( ICodeFixService codeFixService, TextDocument document, TextSpan selection, - ICodeActionRequestPriorityProvider priorityProvider, + CodeActionRequestPriority? priority, CancellationToken cancellationToken) { - 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. await TaskScheduler.Default; var fixes = await codeFixService.GetFixesAsync( document, selection, - priorityProvider, + priority, cancellationToken).ConfigureAwait(false); var filteredFixes = fixes.WhereAsArray(c => c.Fixes.Length > 0); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var organizedFixes = await OrganizeFixesAsync(workspace, originalSolution, text, filteredFixes, cancellationToken).ConfigureAwait(false); + var organizedFixes = await OrganizeFixesAsync(document.Project, text, filteredFixes, cancellationToken).ConfigureAwait(false); return organizedFixes; } @@ -61,21 +60,20 @@ public static async ValueTask> GetFilt /// /// Arrange fixes into groups based on the issue (diagnostic being fixed) and prioritize these groups. /// - private static async Task> OrganizeFixesAsync( - Workspace workspace, - Solution originalSolution, + private static async Task> OrganizeFixesAsync( + Project project, SourceText text, ImmutableArray fixCollections, CancellationToken cancellationToken) { - var map = ImmutableDictionary.CreateBuilder>(); + var map = ImmutableDictionary.CreateBuilder>(); using var _ = ArrayBuilder.GetInstance(out var order); // First group fixes by diagnostic and priority. - await GroupFixesAsync(workspace, originalSolution, fixCollections, map, order, cancellationToken).ConfigureAwait(false); + await GroupFixesAsync(project, fixCollections, map, order, cancellationToken).ConfigureAwait(false); // Then prioritize between the groups. - var prioritizedFixes = PrioritizeFixGroups(originalSolution, text, map.ToImmutable(), order.ToImmutable(), workspace); + var prioritizedFixes = PrioritizeFixGroups(text, map.ToImmutable(), order.ToImmutable()); return prioritizedFixes; } @@ -83,21 +81,19 @@ private static async Task> OrganizeFix /// Groups fixes by the diagnostic being addressed by each fix. /// private static async Task GroupFixesAsync( - Workspace workspace, - Solution originalSolution, + Project project, ImmutableArray fixCollections, - IDictionary> map, + IDictionary> map, ArrayBuilder order, CancellationToken cancellationToken) { foreach (var fixCollection in fixCollections) - await ProcessFixCollectionAsync(workspace, originalSolution, map, order, fixCollection, cancellationToken).ConfigureAwait(false); + await ProcessFixCollectionAsync(project, map, order, fixCollection, cancellationToken).ConfigureAwait(false); } private static async Task ProcessFixCollectionAsync( - Workspace workspace, - Solution originalSolution, - IDictionary> map, + Project project, + IDictionary> map, ArrayBuilder order, CodeFixCollection fixCollection, CancellationToken cancellationToken) @@ -108,125 +104,120 @@ private static async Task ProcessFixCollectionAsync( var nonSupressionCodeFixes = fixes.WhereAsArray(f => !IsTopLevelSuppressionAction(f.Action)); var supressionCodeFixes = fixes.WhereAsArray(f => IsTopLevelSuppressionAction(f.Action)); - await AddCodeActionsAsync(workspace, originalSolution, map, order, fixCollection, GetFixAllSuggestedActionSetAsync, nonSupressionCodeFixes).ConfigureAwait(false); + await AddCodeActionsAsync(project, map, order, fixCollection, GetFlavorsAsync, nonSupressionCodeFixes).ConfigureAwait(false); // Add suppression fixes to the end of a given SuggestedActionSet so that they // always show up last in a group. - await AddCodeActionsAsync(workspace, originalSolution, map, order, fixCollection, GetFixAllSuggestedActionSetAsync, supressionCodeFixes).ConfigureAwait(false); + await AddCodeActionsAsync(project, map, order, fixCollection, GetFlavorsAsync, supressionCodeFixes).ConfigureAwait(false); return; // Local functions - Task GetFixAllSuggestedActionSetAsync(CodeAction codeAction) - => GetUnifiedFixAllSuggestedActionSetAsync( - codeAction, fixCount, fixCollection.FixAllState, - fixCollection.SupportedScopes, fixCollection.FirstDiagnostic, - workspace, originalSolution, cancellationToken); + Task GetFlavorsAsync(CodeAction codeAction) + => GetUnifiedSuggestedActionFlavorsAsync( + codeAction, fixCount, fixCollection.FixAllState, fixCollection.SupportedScopes, fixCollection.Diagnostics, cancellationToken); } private static async Task AddCodeActionsAsync( - Workspace workspace, - Solution originalSolution, - IDictionary> map, + Project project, + IDictionary> map, ArrayBuilder order, CodeFixCollection fixCollection, - Func> getFixAllSuggestedActionSetAsync, + Func> getFixAllSuggestedActionSetAsync, ImmutableArray codeFixes) { foreach (var fix in codeFixes) { - var unifiedSuggestedAction = await GetUnifiedSuggestedActionAsync(originalSolution, fix.Action, fix).ConfigureAwait(false); - AddFix(fix, unifiedSuggestedAction, map, order); + var unifiedSuggestedAction = await GetUnifiedSuggestedActionAsync(project, fix.Action, fix).ConfigureAwait(false); + AddFix(project, fix, unifiedSuggestedAction, map, order); } return; // Local functions - async Task GetUnifiedSuggestedActionAsync(Solution originalSolution, CodeAction action, CodeFix fix) + async Task GetUnifiedSuggestedActionAsync(Project project, CodeAction action, CodeFix fix) { if (action.NestedActions.Length > 0) { - var unifiedNestedActions = new FixedSizeArrayBuilder(action.NestedActions.Length); + var unifiedNestedActions = new FixedSizeArrayBuilder(action.NestedActions.Length); foreach (var nestedAction in action.NestedActions) { - var unifiedNestedAction = await GetUnifiedSuggestedActionAsync(originalSolution, nestedAction, fix).ConfigureAwait(false); + var unifiedNestedAction = await GetUnifiedSuggestedActionAsync(project, nestedAction, fix).ConfigureAwait(false); unifiedNestedActions.Add(unifiedNestedAction); } - var set = new UnifiedSuggestedActionSet( - originalSolution, + var set = new SuggestedActionSet( categoryName: null, actions: unifiedNestedActions.MoveToImmutable(), title: null, priority: action.Priority, - applicableToSpan: fix.PrimaryDiagnostic.Location.SourceSpan); + applicableToSpan: fix.Diagnostics.First().Location.SourceSpan); - return new UnifiedSuggestedActionWithNestedActions( - workspace, action, action.Priority, fixCollection.Provider, [set]); + return SuggestedAction.CreateWithNestedActionSets( + action, action.Priority, fixCollection.Provider, codeRefactoringKind: null, diagnostics: [], [set]); } else { - return new UnifiedCodeFixSuggestedAction( - workspace, action, action.Priority, fix, fixCollection.Provider, + return SuggestedAction.CreateWithFlavors( + action, action.Priority, fixCollection.Provider, codeRefactoringKind: null, fix.Diagnostics, await getFixAllSuggestedActionSetAsync(action).ConfigureAwait(false)); } } } private static void AddFix( - CodeFix fix, IUnifiedSuggestedAction suggestedAction, - IDictionary> map, + Project project, + CodeFix fix, + SuggestedAction suggestedAction, + IDictionary> map, ArrayBuilder order) { - var groupKey = GetGroupKey(fix); + var groupKey = GetGroupKey(fix, project); if (!map.TryGetValue(groupKey, out var suggestedActions)) { order.Add(groupKey); - suggestedActions = ImmutableArray.CreateBuilder(); + suggestedActions = ImmutableArray.CreateBuilder(); map[groupKey] = suggestedActions; } suggestedActions.Add(suggestedAction); return; - static CodeFixGroupKey GetGroupKey(CodeFix fix) + static CodeFixGroupKey GetGroupKey(CodeFix fix, Project project) { - var diag = fix.GetPrimaryDiagnosticData(); + var diagnosticData = DiagnosticData.Create(fix.Diagnostics.First(), project); if (fix.Action is AbstractConfigurationActionWithNestedActions configurationAction) { return new CodeFixGroupKey( - diag, configurationAction.Priority, configurationAction.AdditionalPriority); + diagnosticData, configurationAction.Priority, configurationAction.AdditionalPriority); } - return new CodeFixGroupKey(diag, fix.Action.Priority, null); + return new CodeFixGroupKey(diagnosticData, fix.Action.Priority, null); } } // If the provided fix all context is non-null and the context's code action Id matches // the given code action's Id, returns the set of fix all occurrences actions associated // with the code action. - private static async Task GetUnifiedFixAllSuggestedActionSetAsync( + private static async Task GetUnifiedSuggestedActionFlavorsAsync( CodeAction action, int actionCount, - IFixAllState? fixAllState, + IRefactorOrFixAllState? fixAllState, ImmutableArray supportedScopes, - Diagnostic firstDiagnostic, - Workspace workspace, - Solution originalSolution, + ImmutableArray diagnostics, CancellationToken cancellationToken) { if (fixAllState == null) - { return null; - } if (actionCount > 1 && action.EquivalenceKey == null) - { return null; - } + + if (diagnostics is not [var firstDiagnostic, ..]) + return null; var textDocument = fixAllState.Document!; - using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); + using var _ = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in supportedScopes) { if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) @@ -249,19 +240,13 @@ static CodeFixGroupKey GetGroupKey(CodeFix fix) } var fixAllStateForScope = fixAllState.With(scope: scope, codeActionEquivalenceKey: action.EquivalenceKey); - var fixAllSuggestedAction = new UnifiedFixAllCodeFixSuggestedAction( - workspace, action, action.Priority, fixAllStateForScope, firstDiagnostic); + var fixAllSuggestedAction = SuggestedAction.CreateRefactorOrFixAll( + action, action.Priority, codeRefactoringKind: null, diagnostics, fixAllStateForScope); fixAllSuggestedActions.Add(fixAllSuggestedAction); } - return new UnifiedSuggestedActionSet( - originalSolution, - categoryName: null, - actions: fixAllSuggestedActions.ToImmutable(), - title: CodeFixesResources.Fix_all_occurrences_in, - priority: CodeActionPriority.Lowest, - applicableToSpan: null); + return new(CodeFixesResources.Fix_all_occurrences_in, fixAllSuggestedActions.ToImmutableAndClear()); } /// @@ -269,35 +254,33 @@ static CodeFixGroupKey GetGroupKey(CodeFix fix) /// /// /// Fix groups are returned in priority order determined based on . - /// Priority for all s containing fixes is set to s containing fixes is set to by default. The only exception is the case where a only contains suppression fixes - the priority of such s is set to so that suppression + /// cref="SuggestedActionSet"/> only contains suppression fixes - the priority of such s is set to so that suppression /// fixes always show up last after all other fixes (and refactorings) for the selected line of code. /// - private static ImmutableArray PrioritizeFixGroups( - Solution originalSolution, + private static ImmutableArray PrioritizeFixGroups( SourceText text, - ImmutableDictionary> map, - ImmutableArray order, - Workspace workspace) + ImmutableDictionary> map, + ImmutableArray order) { - using var _1 = ArrayBuilder.GetInstance(out var nonSuppressionSets); - using var _2 = ArrayBuilder.GetInstance(out var suppressionSets); - using var _3 = ArrayBuilder.GetInstance(out var bulkConfigurationActions); + using var _1 = ArrayBuilder.GetInstance(out var nonSuppressionSets); + using var _2 = ArrayBuilder.GetInstance(out var suppressionSets); + using var _3 = ArrayBuilder.GetInstance(out var bulkConfigurationActions); foreach (var groupKey in order) { var actions = map[groupKey]; - var nonSuppressionActions = actions.WhereAsArray(a => !IsTopLevelSuppressionAction(a.OriginalCodeAction)); - AddUnifiedSuggestedActionsSet(originalSolution, text, nonSuppressionActions, groupKey, nonSuppressionSets); + var nonSuppressionActions = actions.WhereAsArray(a => !IsTopLevelSuppressionAction(a.CodeAction)); + AddUnifiedSuggestedActionsSet(text, nonSuppressionActions, groupKey, nonSuppressionSets); - var suppressionActions = actions.WhereAsArray(a => IsTopLevelSuppressionAction(a.OriginalCodeAction) && - !IsBulkConfigurationAction(a.OriginalCodeAction)); - AddUnifiedSuggestedActionsSet(originalSolution, text, suppressionActions, groupKey, suppressionSets); + var suppressionActions = actions.WhereAsArray(a => IsTopLevelSuppressionAction(a.CodeAction) && + !IsBulkConfigurationAction(a.CodeAction)); + AddUnifiedSuggestedActionsSet(text, suppressionActions, groupKey, suppressionSets); - bulkConfigurationActions.AddRange(actions.Where(a => IsBulkConfigurationAction(a.OriginalCodeAction))); + bulkConfigurationActions.AddRange(actions.Where(a => IsBulkConfigurationAction(a.CodeAction))); } var sets = nonSuppressionSets.ToImmutable(); @@ -305,8 +288,7 @@ private static ImmutableArray PrioritizeFixGroups( // Append bulk configuration fixes at the end of suppression/configuration fixes. if (bulkConfigurationActions.Count > 0) { - var bulkConfigurationSet = new UnifiedSuggestedActionSet( - originalSolution, + var bulkConfigurationSet = new SuggestedActionSet( UnifiedPredefinedSuggestedActionCategoryNames.CodeFix, bulkConfigurationActions.ToImmutable(), title: null, @@ -320,16 +302,23 @@ private static ImmutableArray PrioritizeFixGroups( // Wrap the suppression/configuration actions within another top level suggested action // to avoid clutter in the light bulb menu. var suppressOrConfigureCodeAction = NoChangeAction.Create(CodeFixesResources.Suppress_or_configure_issues, nameof(CodeFixesResources.Suppress_or_configure_issues)); - var wrappingSuggestedAction = new UnifiedSuggestedActionWithNestedActions( - workspace, codeAction: suppressOrConfigureCodeAction, - codeActionPriority: suppressOrConfigureCodeAction.Priority, provider: null, - nestedActionSets: suppressionSets.ToImmutable()); + + // Just pass along the provider belonging to the first action that we're offering to suppress/configure. + // This doesn't actually get used as this top level action is just a container for the nested actions + // and is never invoked itself. + var provider = suppressionSets[0].Actions[0].Provider; + var wrappingSuggestedAction = SuggestedAction.CreateWithNestedActionSets( + suppressOrConfigureCodeAction, + suppressOrConfigureCodeAction.Priority, + provider, + codeRefactoringKind: null, + diagnostics: [], + suppressionSets.ToImmutable()); // Combine the spans and the category of each of the nested suggested actions // to get the span and category for the new top level suggested action. var (span, category) = CombineSpansAndCategory(suppressionSets); - var wrappingSet = new UnifiedSuggestedActionSet( - originalSolution, + var wrappingSet = new SuggestedActionSet( category, actions: [wrappingSuggestedAction], title: CodeFixesResources.Suppress_or_configure_issues, @@ -341,7 +330,7 @@ private static ImmutableArray PrioritizeFixGroups( return sets; // Local functions - static (TextSpan? span, string category) CombineSpansAndCategory(ArrayBuilder sets) + static (TextSpan? span, string category) CombineSpansAndCategory(ArrayBuilder sets) { // We are combining the spans and categories of the given set of suggested action sets // to generate a result span containing the spans of individual suggested action sets and @@ -384,25 +373,23 @@ private static ImmutableArray PrioritizeFixGroups( } private static void AddUnifiedSuggestedActionsSet( - Solution originalSolution, SourceText text, - ImmutableArray actions, + ImmutableArray actions, CodeFixGroupKey groupKey, - ArrayBuilder sets) + ArrayBuilder sets) { foreach (var group in actions.GroupBy(a => a.CodeActionPriority)) { var priority = group.Key; // diagnostic from things like build shouldn't reach here since we don't support LB for those diagnostics - var category = GetFixCategory(groupKey.Item1.Severity); - sets.Add(new UnifiedSuggestedActionSet( - originalSolution, + var category = GetFixCategory(groupKey.diagnostic.Severity); + sets.Add(new SuggestedActionSet( category, [.. group], title: null, priority, - applicableToSpan: groupKey.Item1.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))); + applicableToSpan: groupKey.diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))); } } @@ -430,8 +417,7 @@ private static bool IsBulkConfigurationAction(CodeAction action) /// /// Gets, filters, and orders code refactorings. /// - public static async Task> GetFilterAndOrderCodeRefactoringsAsync( - Workspace workspace, + public static async Task> GetFilterAndOrderCodeRefactoringsAsync( ICodeRefactoringService codeRefactoringService, TextDocument document, TextSpan selection, @@ -448,10 +434,10 @@ public static async Task> GetFilterAnd var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); - var orderedRefactorings = new FixedSizeArrayBuilder(filteredRefactorings.Length); + var orderedRefactorings = new FixedSizeArrayBuilder(filteredRefactorings.Length); foreach (var refactoring in filteredRefactorings) { - var orderedRefactoring = await OrganizeRefactoringsAsync(workspace, document, selection, refactoring, cancellationToken).ConfigureAwait(false); + var orderedRefactoring = await OrganizeRefactoringsAsync(document, selection, refactoring, cancellationToken).ConfigureAwait(false); orderedRefactorings.Add(orderedRefactoring); } @@ -497,20 +483,17 @@ bool IsActionAndSpanApplicable((CodeAction action, TextSpan? applicableSpan) act /// /// /// Refactorings are returned in priority order determined based on . - /// Priority for all s containing refactorings is set to + /// Priority for all s containing refactorings is set to /// and should show up after fixes but before /// suppression fixes in the light bulb menu. /// - private static async Task OrganizeRefactoringsAsync( - Workspace workspace, + private static async Task OrganizeRefactoringsAsync( TextDocument document, TextSpan selection, CodeRefactoring refactoring, CancellationToken cancellationToken) { - var originalSolution = document.Project.Solution; - - using var _ = ArrayBuilder.GetInstance(out var refactoringSuggestedActions); + var refactoringSuggestedActions = new FixedSizeArrayBuilder(refactoring.CodeActions.Length); foreach (var (action, applicableToSpan) in refactoring.CodeActions) { @@ -518,7 +501,7 @@ private static async Task OrganizeRefactoringsAsync( refactoringSuggestedActions.Add(unifiedActionSet); } - var actions = refactoringSuggestedActions.ToImmutable(); + var actions = refactoringSuggestedActions.MoveToImmutable(); // An action set: // - gets the the same priority as the highest priority action within in. @@ -527,46 +510,44 @@ private static async Task OrganizeRefactoringsAsync( // choice. All actions created by one Refactoring have usually the same `applicableSpan` // and therefore the complexity of determining the closest one isn't worth the benefit // of slightly more correct orderings in certain edge cases. - return new UnifiedSuggestedActionSet( - originalSolution, - UnifiedPredefinedSuggestedActionCategoryNames.Refactoring, - actions: actions, + return new SuggestedActionSet( + categoryName: UnifiedPredefinedSuggestedActionCategoryNames.Refactoring, + actions, title: null, priority: actions.Max(a => a.CodeActionPriority), - applicableToSpan: refactoring.CodeActions.FirstOrDefault().applicableToSpan); + refactoring.CodeActions.FirstOrDefault().applicableToSpan); // Local functions - async Task GetUnifiedSuggestedActionSetAsync(CodeAction codeAction, TextSpan? applicableToSpan, TextSpan selection, CancellationToken cancellationToken) + async Task GetUnifiedSuggestedActionSetAsync(CodeAction codeAction, TextSpan? applicableToSpan, TextSpan selection, CancellationToken cancellationToken) { if (codeAction.NestedActions.Length > 0) { - var nestedActions = new FixedSizeArrayBuilder(codeAction.NestedActions.Length); + var nestedActions = new FixedSizeArrayBuilder(codeAction.NestedActions.Length); foreach (var nestedAction in codeAction.NestedActions) { var unifiedAction = await GetUnifiedSuggestedActionSetAsync(nestedAction, applicableToSpan, selection, cancellationToken).ConfigureAwait(false); nestedActions.Add(unifiedAction); } - var set = new UnifiedSuggestedActionSet( - originalSolution, + var set = new SuggestedActionSet( categoryName: null, actions: nestedActions.MoveToImmutable(), title: null, priority: codeAction.Priority, applicableToSpan: applicableToSpan); - return new UnifiedSuggestedActionWithNestedActions( - workspace, codeAction, codeAction.Priority, refactoring.Provider, [set]); + return SuggestedAction.CreateWithNestedActionSets( + codeAction, codeAction.Priority, refactoring.Provider, codeRefactoringKind: null, diagnostics: [], [set]); } else { var fixAllSuggestedActionSet = await GetUnifiedFixAllSuggestedActionSetAsync(codeAction, refactoring.CodeActions.Length, document as Document, selection, refactoring.Provider, - refactoring.FixAllProviderInfo, - workspace, cancellationToken).ConfigureAwait(false); + refactoring.FixAllProviderInfo, cancellationToken).ConfigureAwait(false); - return new UnifiedCodeRefactoringSuggestedAction( - workspace, codeAction, codeAction.Priority, refactoring.Provider, fixAllSuggestedActionSet); + return SuggestedAction.CreateWithFlavors( + codeAction, codeAction.Priority, refactoring.Provider, refactoring.Provider.Kind, + diagnostics: [], fixAllSuggestedActionSet); } } } @@ -574,61 +555,48 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction // If the provided fix all context is non-null and the context's code action Id matches // the given code action's Id, returns the set of fix all occurrences actions associated // with the code action. - private static async Task GetUnifiedFixAllSuggestedActionSetAsync( + private static async Task GetUnifiedFixAllSuggestedActionSetAsync( CodeAction action, int actionCount, Document? document, TextSpan selection, CodeRefactoringProvider provider, FixAllProviderInfo? fixAllProviderInfo, - Workspace workspace, CancellationToken cancellationToken) { if (fixAllProviderInfo == null || document == null) - { return null; - } // If the provider registered more than one code action, but provided a null equivalence key // we have no way to distinguish between which registered actions to apply or ignore for FixAll. // So, we just bail out for this case. if (actionCount > 1 && action.EquivalenceKey == null) - { return null; - } - var originalSolution = document.Project.Solution; - - using var fixAllSuggestedActionsDisposer = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); + using var _ = ArrayBuilder.GetInstance(out var fixAllSuggestedActions); foreach (var scope in fixAllProviderInfo.SupportedScopes) { - var fixAllState = new CodeRefactorings.FixAllState( - (CodeRefactorings.FixAllProvider)fixAllProviderInfo.FixAllProvider, - document, selection, provider, scope, action); + var fixAllState = new RefactorAllState( + (RefactorAllProvider)fixAllProviderInfo.FixAllProvider, + document, selection, provider, scope.ToRefactorAllScope(), action); if (scope is FixAllScope.ContainingMember or FixAllScope.ContainingType) { // Skip showing ContainingMember and ContainingType FixAll scopes if the language // does not implement 'IFixAllSpanMappingService' langauge service or // we have no mapped FixAll spans to fix. - var documentsAndSpans = await fixAllState.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); + var documentsAndSpans = await fixAllState.GetRefactorAllSpansAsync(cancellationToken).ConfigureAwait(false); if (documentsAndSpans.IsEmpty) continue; } - var fixAllSuggestedAction = new UnifiedFixAllCodeRefactoringSuggestedAction( - workspace, action, action.Priority, fixAllState); + var fixAllSuggestedAction = SuggestedAction.CreateRefactorOrFixAll( + action, action.Priority, provider.Kind, diagnostics: [], fixAllState); fixAllSuggestedActions.Add(fixAllSuggestedAction); } - return new UnifiedSuggestedActionSet( - originalSolution, - categoryName: null, - actions: fixAllSuggestedActions.ToImmutable(), - title: CodeFixesResources.Fix_all_occurrences_in, - priority: CodeActionPriority.Lowest, - applicableToSpan: null); + return new(CodeFixesResources.Fix_all_occurrences_in, fixAllSuggestedActions.ToImmutableAndClear()); } /// @@ -636,9 +604,9 @@ async Task GetUnifiedSuggestedActionSetAsync(CodeAction /// Should be called with the results from /// and . /// - public static ImmutableArray FilterAndOrderActionSets( - ImmutableArray fixes, - ImmutableArray refactorings, + public static ImmutableArray FilterAndOrderActionSets( + ImmutableArray fixes, + ImmutableArray refactorings, TextSpan? selectionOpt, int currentActionCount) { @@ -657,10 +625,10 @@ public static ImmutableArray FilterAndOrderActionSets return filteredSets; } - private static ImmutableArray GetInitiallyOrderedActionSets( + private static ImmutableArray GetInitiallyOrderedActionSets( TextSpan? selectionOpt, - ImmutableArray fixes, - ImmutableArray refactorings) + ImmutableArray fixes, + ImmutableArray refactorings) { // First, order refactorings based on the order the providers actually gave for // their actions. This way, a low pri refactoring always shows after a medium pri @@ -701,18 +669,18 @@ private static ImmutableArray GetInitiallyOrderedActi } } - private static ImmutableArray OrderActionSets( - ImmutableArray actionSets, TextSpan? selectionOpt) + private static ImmutableArray OrderActionSets( + ImmutableArray actionSets, TextSpan? selectionOpt) { return [.. actionSets.OrderByDescending(s => s.Priority).ThenBy(s => s, new UnifiedSuggestedActionSetComparer(selectionOpt))]; } - private static UnifiedSuggestedActionSet WithPriority( - UnifiedSuggestedActionSet set, CodeActionPriority priority) - => new(set.OriginalSolution, set.CategoryName, set.Actions, set.Title, priority, set.ApplicableToSpan); + private static SuggestedActionSet WithPriority( + SuggestedActionSet set, CodeActionPriority priority) + => new(set.CategoryName, set.Actions, set.Title, priority, set.ApplicableToSpan); - private static ImmutableArray InlineActionSetsIfDesirable( - ImmutableArray actionSets, + private static ImmutableArray InlineActionSetsIfDesirable( + ImmutableArray actionSets, int currentActionCount) { // If we only have a single set of items, and that set only has three max suggestion @@ -723,17 +691,15 @@ private static ImmutableArray InlineActionSetsIfDesir : actionSets.SelectAsArray(InlineActions); } - private static UnifiedSuggestedActionSet InlineActions(UnifiedSuggestedActionSet actionSet) + private static SuggestedActionSet InlineActions(SuggestedActionSet actionSet) { - using var newActionsDisposer = ArrayBuilder.GetInstance(out var newActions); + using var newActionsDisposer = ArrayBuilder.GetInstance(out var newActions); foreach (var action in actionSet.Actions) { - var actionWithNestedActions = action as UnifiedSuggestedActionWithNestedActions; - // Only inline if the underlying code action allows it. - if (actionWithNestedActions?.OriginalCodeAction.IsInlinable == true) + if (action is { CodeAction.IsInlinable: true, NestedActionSets.Length: > 0 }) { - newActions.AddRange(actionWithNestedActions.NestedActionSets.SelectMany(set => set.Actions)); + newActions.AddRange(action.NestedActionSets.SelectMany(set => set.Actions)); } else { @@ -741,8 +707,7 @@ private static UnifiedSuggestedActionSet InlineActions(UnifiedSuggestedActionSet } } - return new UnifiedSuggestedActionSet( - actionSet.OriginalSolution, + return new SuggestedActionSet( actionSet.CategoryName, newActions.ToImmutable(), actionSet.Title, @@ -750,10 +715,10 @@ private static UnifiedSuggestedActionSet InlineActions(UnifiedSuggestedActionSet actionSet.ApplicableToSpan); } - private static ImmutableArray FilterActionSetsByTitle( - ImmutableArray allActionSets) + private static ImmutableArray FilterActionSetsByTitle( + ImmutableArray allActionSets) { - using var resultDisposer = ArrayBuilder.GetInstance(out var result); + using var resultDisposer = ArrayBuilder.GetInstance(out var result); var seenTitles = new HashSet(); foreach (var set in allActionSets) @@ -768,13 +733,13 @@ private static ImmutableArray FilterActionSetsByTitle return result.ToImmutableAndClear(); } - private static UnifiedSuggestedActionSet? FilterActionSetByTitle(UnifiedSuggestedActionSet set, HashSet seenTitles) + private static SuggestedActionSet? FilterActionSetByTitle(SuggestedActionSet set, HashSet seenTitles) { - using var actionsDisposer = ArrayBuilder.GetInstance(out var actions); + using var _ = ArrayBuilder.GetInstance(out var actions); foreach (var action in set.Actions) { - if (seenTitles.Add(action.OriginalCodeAction.Title)) + if (seenTitles.Add(action.CodeAction.Title)) { actions.Add(action); } @@ -782,6 +747,6 @@ private static ImmutableArray FilterActionSetsByTitle return actions.Count == 0 ? null - : new UnifiedSuggestedActionSet(set.OriginalSolution, set.CategoryName, actions.ToImmutable(), set.Title, set.Priority, set.ApplicableToSpan); + : new SuggestedActionSet(set.CategoryName, actions.ToImmutable(), set.Title, set.Priority, set.ApplicableToSpan); } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs deleted file mode 100644 index e2ee5098bcc..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionSet.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to SuggestedActionSet, but in a location that can be used -/// by both local Roslyn and LSP. -/// -internal sealed class UnifiedSuggestedActionSet -{ - public Solution OriginalSolution { get; } - - public string? CategoryName { get; } - - public ImmutableArray Actions { get; } - - public object? Title { get; } - - public CodeActionPriority Priority { get; } - - public TextSpan? ApplicableToSpan { get; } - - public UnifiedSuggestedActionSet( - Solution originalSolution, - string? categoryName, - ImmutableArray actions, - object? title, - CodeActionPriority priority, - TextSpan? applicableToSpan) - { - OriginalSolution = originalSolution; - CategoryName = categoryName; - Actions = actions; - Title = title; - Priority = priority; - ApplicableToSpan = applicableToSpan; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs deleted file mode 100644 index 728187b5c68..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeFixSuggestedAction.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeFixes; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of CodeFixSuggestedAction. -/// -internal interface ICodeFixSuggestedAction -{ - CodeFix CodeFix { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs deleted file mode 100644 index cc34589aabb..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/ICodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeRefactorings; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of CodeRefactoringSuggestedAction. -/// -internal interface ICodeRefactoringSuggestedAction -{ - CodeRefactoringProvider CodeRefactoringProvider { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs deleted file mode 100644 index 6147f51263b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeFixSuggestedAction.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of FixAllCodeFixSuggestedAction. -/// -internal interface IFixAllCodeFixSuggestedAction -{ - Diagnostic Diagnostic { get; } - - CodeAction OriginalCodeAction { get; } - - IFixAllState FixAllState { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.cs deleted file mode 100644 index a044aa57661..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IFixAllCodeRefactoringSuggestedAction.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. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -/// -/// Common interface used by both local Roslyn and LSP to implement -/// their specific versions of FixAllCodeRefactoringSuggestedAction. -/// -internal interface IFixAllCodeRefactoringSuggestedAction -{ - CodeAction OriginalCodeAction { get; } - - IFixAllState FixAllState { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.cs deleted file mode 100644 index 62afaa4a830..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/IUnifiedSuggestedAction.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. - -using Microsoft.CodeAnalysis.CodeActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to ISuggestedAction, but in a location that can be used by both local Roslyn and LSP. -/// -internal interface IUnifiedSuggestedAction -{ - Workspace Workspace { get; } - - CodeAction OriginalCodeAction { get; } - - CodeActionPriority CodeActionPriority { get; } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs deleted file mode 100644 index 2725cd3f17e..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeFixSuggestedAction.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to CodeFixSuggestionAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedCodeFixSuggestedAction : UnifiedSuggestedAction, ICodeFixSuggestedAction -{ - public CodeFix CodeFix { get; } - - public object Provider { get; } - - public UnifiedSuggestedActionSet? FixAllFlavors { get; } - - public UnifiedCodeFixSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - CodeFix codeFix, - object provider, - UnifiedSuggestedActionSet? fixAllFlavors) - : base(workspace, codeAction, codeActionPriority) - { - CodeFix = codeFix; - Provider = provider; - FixAllFlavors = fixAllFlavors; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs deleted file mode 100644 index 70751f41b2b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to CodeRefactoringSuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedCodeRefactoringSuggestedAction : UnifiedSuggestedAction, ICodeRefactoringSuggestedAction -{ - public CodeRefactoringProvider CodeRefactoringProvider { get; } - - public UnifiedSuggestedActionSet? FixAllFlavors { get; } - - public UnifiedCodeRefactoringSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - CodeRefactoringProvider codeRefactoringProvider, - UnifiedSuggestedActionSet? fixAllFlavors) - : base(workspace, codeAction, codeActionPriority) - { - CodeRefactoringProvider = codeRefactoringProvider; - FixAllFlavors = fixAllFlavors; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs deleted file mode 100644 index 652590f7872..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to FixAllCodeFixSuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedFixAllCodeFixSuggestedAction : UnifiedSuggestedAction, IFixAllCodeFixSuggestedAction -{ - public Diagnostic Diagnostic { get; } - - public IFixAllState FixAllState { get; } - - public UnifiedFixAllCodeFixSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - IFixAllState fixAllState, - Diagnostic diagnostic) - : base(workspace, codeAction, codeActionPriority) - { - Diagnostic = diagnostic; - FixAllState = fixAllState; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs deleted file mode 100644 index ed7bb22c98b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to FixAllCodeRefactoringSuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedFixAllCodeRefactoringSuggestedAction : UnifiedSuggestedAction, IFixAllCodeRefactoringSuggestedAction -{ - public IFixAllState FixAllState { get; } - - public UnifiedFixAllCodeRefactoringSuggestedAction( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - IFixAllState fixAllState) - : base(workspace, codeAction, codeActionPriority) - { - FixAllState = fixAllState; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs deleted file mode 100644 index 17e04ceeb9b..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis.CodeActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to SuggestedAction, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal class UnifiedSuggestedAction(Workspace workspace, CodeAction codeAction, CodeActionPriority codeActionPriority) : IUnifiedSuggestedAction -{ - public Workspace Workspace { get; } = workspace; - - public CodeAction OriginalCodeAction { get; } = codeAction; - - public CodeActionPriority CodeActionPriority { get; } = codeActionPriority; -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs b/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs deleted file mode 100644 index dfb653852d1..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedActionWithNestedActions.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeActions; - -namespace Microsoft.CodeAnalysis.UnifiedSuggestions; - -/// -/// Similar to SuggestedActionWithNestedActions, but in a location that can be used by -/// both local Roslyn and LSP. -/// -internal sealed class UnifiedSuggestedActionWithNestedActions : UnifiedSuggestedAction -{ - public object? Provider { get; } - - public ImmutableArray NestedActionSets { get; } - - public UnifiedSuggestedActionWithNestedActions( - Workspace workspace, - CodeAction codeAction, - CodeActionPriority codeActionPriority, - object? provider, - ImmutableArray nestedActionSets) - : base(workspace, codeAction, codeActionPriority) - { - Provider = provider; - NestedActionSets = nestedActionSets; - } -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs index 44c598d0290..7b7079da949 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/BufferedProgress.cs @@ -67,7 +67,7 @@ public void Report(T value) internal static class BufferedProgress { public static BufferedProgress Create(IProgress? progress) - => new BufferedProgress(progress); + => new(progress); public static BufferedProgress Create(IProgress? progress, Func transform) => Create(progress?.Transform(transform)); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs index d6adf6f9104..d067884d0f8 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; @@ -53,7 +54,7 @@ public async Task HandleRequestAsync(RoslynFixAllCodeAct Contract.ThrowIfNull(data.CodeActionPath); var codeActionToResolve = CodeActionHelpers.GetCodeActionToResolve(data.CodeActionPath, codeActions, isFixAllAction: true); - var fixAllCodeAction = (FixAllCodeAction)codeActionToResolve; + var fixAllCodeAction = (RefactorOrFixAllCodeAction)codeActionToResolve; Contract.ThrowIfNull(fixAllCodeAction); var operations = await fixAllCodeAction.GetOperationsAsync(document.Project.Solution, CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 4e856d34cd0..c4a5481acd3 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs @@ -9,13 +9,13 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.ExtractClass; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.UnifiedSuggestions; -using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; +using Microsoft.CodeAnalysis.Suggestions; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using CodeAction = Microsoft.CodeAnalysis.CodeActions.CodeAction; @@ -93,26 +93,26 @@ internal static class CodeActionHelpers return codeActions.ToArray(); } - private static bool IsCodeActionNotSupportedByLSP(IUnifiedSuggestedAction suggestedAction) + private static bool IsCodeActionNotSupportedByLSP(SuggestedAction suggestedAction) // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options. // Exceptions are made for ExtractClass and ExtractInterface because we have OptionsServices which // provide reasonable defaults without user interaction. - => (suggestedAction.OriginalCodeAction is CodeActionWithOptions - && suggestedAction.OriginalCodeAction is not ExtractInterfaceCodeAction - && suggestedAction.OriginalCodeAction is not ExtractClassWithDialogCodeAction) + => (suggestedAction.CodeAction is CodeActionWithOptions + && suggestedAction.CodeAction is not ExtractInterfaceCodeAction + && suggestedAction.CodeAction is not ExtractClassWithDialogCodeAction) // Skip code actions that requires non-document changes. We can't apply them in LSP currently. // https://github.com/dotnet/roslyn/issues/48698 - || suggestedAction.OriginalCodeAction.Tags.Contains(CodeAction.RequiresNonDocumentChange); + || suggestedAction.CodeAction.Tags.Contains(CodeAction.RequiresNonDocumentChange); /// /// Generate the matching code actions for . If it contains nested code actions, flatten them into an array. /// private static LSP.CodeAction[] GenerateCodeActions( CodeActionParams request, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, LSP.CodeActionKind codeActionKind) { - var codeAction = suggestedAction.OriginalCodeAction; + var codeAction = suggestedAction.CodeAction; var diagnosticsForFix = GetApplicableDiagnostics(request.Context, suggestedAction); using var _ = ArrayBuilder.GetInstance(out var builder); @@ -142,21 +142,21 @@ private static LSP.CodeAction[] GenerateCodeActions( CodeActionParams request, LSP.CodeActionKind codeActionKind, LSP.Diagnostic[]? diagnosticsForFix, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, ImmutableArray pathOfParentAction, bool isTopLevelCodeAction = false) { - var codeAction = suggestedAction.OriginalCodeAction; + var codeAction = suggestedAction.CodeAction; using var _1 = ArrayBuilder.GetInstance(out var nestedCodeActions); - if (suggestedAction is UnifiedSuggestedActionWithNestedActions unifiedSuggestedActions) + if (!suggestedAction.NestedActionSets.IsEmpty) { - foreach (var actionSet in unifiedSuggestedActions.NestedActionSets) + foreach (var actionSet in suggestedAction.NestedActionSets) { foreach (var action in actionSet.Actions) { nestedCodeActions.AddRange(CollectNestedActions(request, codeActionKind, - diagnosticsForFix, action, pathOfParentAction.Add(action.OriginalCodeAction.Title))); + diagnosticsForFix, action, pathOfParentAction.Add(action.CodeAction.Title))); } } } @@ -181,7 +181,7 @@ private static void AddLSPCodeActions( Command? nestedCodeActionCommand, ImmutableArray? nestedCodeActions, string[] codeActionPath, - IUnifiedSuggestedAction suggestedAction) + SuggestedAction suggestedAction) { var title = codeAction.Title; // We add an overarching action to the lightbulb that may contain nested actions. @@ -196,15 +196,15 @@ private static void AddLSPCodeActions( Data = new CodeActionResolveData(title, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, fixAllFlavors: null, nestedCodeActions) }); - if (suggestedAction is UnifiedCodeFixSuggestedAction unifiedCodeFixSuggestedAction && unifiedCodeFixSuggestedAction.FixAllFlavors is not null) + if (suggestedAction is SuggestedAction { Flavors: { } fixAllFlavors }) { - var fixAllFlavors = unifiedCodeFixSuggestedAction.FixAllFlavors.Actions.OfType().Select(action => action.FixAllState.Scope.ToString()); + var flavorStrings = fixAllFlavors.Actions.Select(a => a.RefactorOrFixAllState?.Scope.ToString()).WhereNotNull(); var fixAllTitle = string.Format(FeaturesResources.Fix_All_0, title); var command = new LSP.Command { CommandIdentifier = CodeActionsHandler.RunFixAllCodeActionCommandName, Title = fixAllTitle, - Arguments = [new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. fixAllFlavors], nestedCodeActions: null)] + Arguments = [new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. flavorStrings], nestedCodeActions: null)] }; builder.Add(new LSP.CodeAction @@ -213,14 +213,14 @@ private static void AddLSPCodeActions( Command = command, Kind = codeActionKind, Diagnostics = diagnosticsForFix, - Data = new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. fixAllFlavors], nestedCodeActions: null) + Data = new CodeActionResolveData(fixAllTitle, codeAction.CustomTags, request.Range, request.TextDocument, codeActionPath, [.. flavorStrings], nestedCodeActions: null) }); } } private static VSInternalCodeAction GenerateVSCodeAction( CodeActionParams request, SourceText documentText, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, LSP.CodeActionKind codeActionKind, CodeActionPriority setPriority, LSP.Range? applicableRange, @@ -228,7 +228,7 @@ private static VSInternalCodeAction GenerateVSCodeAction( ImmutableArray codeActionPathList, ref int currentHighestSetNumber) { - var codeAction = suggestedAction.OriginalCodeAction; + var codeAction = suggestedAction.CodeAction; var diagnosticsForFix = GetApplicableDiagnostics(request.Context, suggestedAction); codeActionPathList = codeActionPathList.Add(codeAction.Title); @@ -250,18 +250,16 @@ private static VSInternalCodeAction GenerateVSCodeAction( static VSInternalCodeAction[] GenerateNestedVSCodeActions( CodeActionParams request, SourceText documentText, - IUnifiedSuggestedAction suggestedAction, + SuggestedAction suggestedAction, CodeActionKind codeActionKind, ref int currentHighestSetNumber, ImmutableArray codeActionPath) { - if (suggestedAction is not UnifiedSuggestedActionWithNestedActions suggestedActionWithNestedActions) - { + if (suggestedAction.NestedActionSets.IsEmpty) return []; - } using var _ = ArrayBuilder.GetInstance(out var nestedActions); - foreach (var nestedActionSet in suggestedActionWithNestedActions.NestedActionSets) + foreach (var nestedActionSet in suggestedAction.NestedActionSets) { // Nested code action sets should each have a unique set number that is not yet assigned to any set. var nestedSetNumber = ++currentHighestSetNumber; @@ -279,13 +277,13 @@ static VSInternalCodeAction[] GenerateNestedVSCodeActions( } } - private static LSP.Diagnostic[]? GetApplicableDiagnostics(CodeActionContext context, IUnifiedSuggestedAction action) + private static LSP.Diagnostic[]? GetApplicableDiagnostics(CodeActionContext context, SuggestedAction action) { - if (action is UnifiedCodeFixSuggestedAction codeFixAction && context.Diagnostics != null) + if (action is SuggestedAction { Diagnostics.Length: > 0 } codeFixAction && context.Diagnostics != null) { // Associate the diagnostics from the request that match the diagnostic fixed by the code action by ID. // The request diagnostics are already restricted to the code fix location by the request. - var diagnosticCodesFixedByAction = codeFixAction.CodeFix.Diagnostics.Select(d => d.Id); + var diagnosticCodesFixedByAction = codeFixAction.Diagnostics.Select(d => d.Id); using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); foreach (var requestDiagnostic in context.Diagnostics) { @@ -346,46 +344,41 @@ public static async Task> GetCodeActionsAsync( /// /// Generates a code action with its nested actions properly set. /// - private static CodeAction GetNestedActionsFromActionSet(IUnifiedSuggestedAction suggestedAction, string? fixAllScope) + private static CodeAction GetNestedActionsFromActionSet(SuggestedAction suggestedAction, string? fixAllScope) { - var codeAction = suggestedAction.OriginalCodeAction; - if (suggestedAction is not UnifiedSuggestedActionWithNestedActions suggestedActionWithNestedActions) - { + var codeAction = suggestedAction.CodeAction; + if (suggestedAction.NestedActionSets.IsEmpty) return codeAction; - } using var _ = ArrayBuilder.GetInstance(out var nestedActions); - foreach (var actionSet in suggestedActionWithNestedActions.NestedActionSets) + foreach (var actionSet in suggestedAction.NestedActionSets) { foreach (var action in actionSet.Actions) { nestedActions.Add(GetNestedActionsFromActionSet(action, fixAllScope)); if (fixAllScope != null) - { GetFixAllActionsFromActionSet(action, nestedActions, fixAllScope); - } } } return CodeAction.Create( - codeAction.Title, nestedActions.ToImmutable(), codeAction.IsInlinable, codeAction.Priority); + codeAction.Title, nestedActions.ToImmutableAndClear(), codeAction.IsInlinable, codeAction.Priority); } - private static void GetFixAllActionsFromActionSet(IUnifiedSuggestedAction suggestedAction, ArrayBuilder codeActions, string? fixAllScope) + private static void GetFixAllActionsFromActionSet(SuggestedAction suggestedAction, ArrayBuilder codeActions, string? fixAllScope) { - var codeAction = suggestedAction.OriginalCodeAction; - if (suggestedAction is not UnifiedCodeFixSuggestedAction { FixAllFlavors: not null } unifiedCodeFixSuggestedAction) - { + var codeAction = suggestedAction.CodeAction; + if (suggestedAction.Flavors is null) return; - } // Retrieves the fix all code action based on the scope that was selected. // Creates a FixAllCodeAction type so that we can get the correct operations for the selected scope. - var fixAllFlavor = unifiedCodeFixSuggestedAction.FixAllFlavors.Actions.OfType().Where(action => action.FixAllState.Scope.ToString() == fixAllScope).First(); - codeActions.Add(new FixAllCodeAction(codeAction.Title, fixAllFlavor.FixAllState, showPreviewChangesDialog: false)); + var fixAllFlavor = suggestedAction.Flavors.Value.Actions.Where(a => a.RefactorOrFixAllState != null && a.RefactorOrFixAllState.Scope.ToString() == fixAllScope).First(); + codeActions.Add(new RefactorOrFixAllCodeAction( + fixAllFlavor.RefactorOrFixAllState!, showPreviewChangesDialog: false, title: codeAction.Title)); } - private static async ValueTask> GetActionSetsAsync( + private static async ValueTask> GetActionSetsAsync( TextDocument document, ICodeFixService codeFixService, ICodeRefactoringService codeRefactoringService, @@ -396,12 +389,10 @@ private static async ValueTask> GetAct var textSpan = ProtocolConversions.RangeToTextSpan(selection, text); var codeFixes = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync( - document.Project.Solution.Workspace, codeFixService, document, textSpan, - new DefaultCodeActionRequestPriorityProvider(), - cancellationToken).ConfigureAwait(false); + codeFixService, document, textSpan, priority: null, cancellationToken).ConfigureAwait(false); var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( - document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, priority: null, + codeRefactoringService, document, textSpan, priority: null, filterOutsideSelection: false, cancellationToken).ConfigureAwait(false); var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets( @@ -409,7 +400,7 @@ private static async ValueTask> GetAct return actionSets; } - private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(string categoryName, IUnifiedSuggestedAction suggestedAction) + private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(string categoryName, SuggestedAction suggestedAction) => categoryName switch { UnifiedPredefinedSuggestedActionCategoryNames.CodeFix => CodeActionKind.QuickFix, @@ -419,18 +410,13 @@ private static CodeActionKind GetCodeActionKindFromSuggestedActionCategoryName(s _ => throw ExceptionUtilities.UnexpectedValue(categoryName) }; - private static CodeActionKind GetRefactoringKind(IUnifiedSuggestedAction suggestedAction) - { - if (suggestedAction is not ICodeRefactoringSuggestedAction refactoringAction) - return CodeActionKind.Refactor; - - return refactoringAction.CodeRefactoringProvider.Kind switch + private static CodeActionKind GetRefactoringKind(SuggestedAction suggestedAction) + => suggestedAction.CodeRefactoringKind switch { CodeRefactoringKind.Extract => CodeActionKind.RefactorExtract, CodeRefactoringKind.Inline => CodeActionKind.RefactorInline, _ => CodeActionKind.Refactor, }; - } private static LSP.VSInternalPriorityLevel? UnifiedSuggestedActionSetPriorityToPriorityLevel(CodeActionPriority priority) => priority switch @@ -461,7 +447,7 @@ public static CodeAction GetCodeActionToResolve(string[] codeActionPath, Immutab // Otherwise, we are likely at the end of the path and need to retrieve // the FixAllCodeAction if we are in that state or just the regular CodeAction // since they have the same title path. - matchingAction = matchingActions.Single(action => isFixAllAction ? action is FixAllCodeAction : action is CodeAction); + matchingAction = matchingActions.Single(action => isFixAllAction ? action is RefactorOrFixAllCodeAction : action is CodeAction); } Contract.ThrowIfNull(matchingAction); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs deleted file mode 100644 index 7b2ab074dca..00000000000 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/CodeActions/FixAllCodeAction.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; - -namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; - -internal sealed class FixAllCodeAction : AbstractFixAllCodeAction -{ - private readonly string _title; - - public FixAllCodeAction(string title, IFixAllState fixAllState, bool showPreviewChangesDialog) : base(fixAllState, showPreviewChangesDialog) - { - _title = title; - } - - public override string Title - => _title; - - protected override IFixAllContext CreateFixAllContext(IFixAllState fixAllState, IProgress progressTracker, CancellationToken cancellationToken) - => new FixAllContext((FixAllState)fixAllState, progressTracker, cancellationToken); - - protected override bool IsInternalProvider(IFixAllState fixAllState) - => true; // FixAll support is internal for the language server. -} diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs index e67b79b27d7..0ed0cc6349c 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -24,56 +23,33 @@ internal sealed record class BuildOnlyDiagnosticIdsResult([property: JsonPropert [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class BuildOnlyDiagnosticIdsHandler( - DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, [ImportMany] IEnumerable> compilerBuildOnlyDiagnosticsProviders) - : ILspServiceRequestHandler + : ILspServiceRequestHandler { public const string BuildOnlyDiagnosticIdsMethodName = "workspace/buildOnlyDiagnosticIds"; - private readonly DiagnosticAnalyzerInfoCache.SharedGlobalCache _globalCache = globalCache; private readonly ImmutableDictionary _compilerBuildOnlyDiagnosticIds = compilerBuildOnlyDiagnosticsProviders .ToImmutableDictionary(lazy => lazy.Metadata.LanguageName, lazy => lazy.Metadata.BuildOnlyDiagnostics); public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public Task HandleRequestAsync(RequestContext context, CancellationToken cancellationToken) + public async Task HandleRequestAsync(RequestContext context, CancellationToken cancellationToken) { - Contract.ThrowIfNull(context.Solution); + var solution = context.Solution; + Contract.ThrowIfNull(solution); - using var _1 = ArrayBuilder.GetInstance(out var builder); - foreach (var languageName in context.Solution.Projects.Select(p => p.Language).Distinct()) + using var _ = ArrayBuilder.GetInstance(out var builder); + foreach (var languageName in solution.Projects.Select(p => p.Language).Distinct()) { if (_compilerBuildOnlyDiagnosticIds.TryGetValue(languageName, out var compilerBuildOnlyDiagnosticIds)) - { builder.AddRange(compilerBuildOnlyDiagnosticIds); - } } - using var _2 = PooledHashSet<(object Reference, string Language)>.GetInstance(out var seenAnalyzerReferencesByLanguage); + var diagnosticService = solution.Services.GetRequiredService(); + builder.AddRange(await diagnosticService.GetCompilationEndDiagnosticDescriptorIdsAsync( + solution, cancellationToken).ConfigureAwait(false)); - foreach (var project in context.Solution.Projects) - { - var analyzersPerReferenceMap = context.Solution.SolutionState.Analyzers.CreateDiagnosticAnalyzersPerReference(project); - foreach (var (analyzerReference, analyzers) in analyzersPerReferenceMap) - { - if (!seenAnalyzerReferencesByLanguage.Add((analyzerReference, project.Language))) - continue; - - foreach (var analyzer in analyzers) - { - // We have already added the compiler build-only diagnostics upfront. - if (analyzer.IsCompilerAnalyzer()) - continue; - - foreach (var buildOnlyDescriptor in _globalCache.AnalyzerInfoCache.GetCompilationEndDiagnosticDescriptors(analyzer)) - { - builder.Add(buildOnlyDescriptor.Id); - } - } - } - } - - return Task.FromResult(new BuildOnlyDiagnosticIdsResult(builder.ToArray())); + return new BuildOnlyDiagnosticIdsResult(builder.ToArray()); } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs index d6f95565292..c593cab2cdc 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSourceProviders/WorkspaceDocumentsAndProjectDiagnosticSourceProvider.cs @@ -67,8 +67,9 @@ async Task AddDocumentsAndProjectAsync(Project project, CancellationToken cancel if (!fullSolutionAnalysisEnabled && !codeAnalysisService.HasProjectBeenAnalyzed(project.Id)) return; - Func? shouldIncludeAnalyzer = !compilerFullSolutionAnalysisEnabled || !analyzersFullSolutionAnalysisEnabled - ? ShouldIncludeAnalyzer : null; + var filter = + (compilerFullSolutionAnalysisEnabled ? AnalyzerFilter.CompilerAnalyzer : 0) | + (analyzersFullSolutionAnalysisEnabled ? AnalyzerFilter.NonCompilerAnalyzer : 0); AddDocumentSources(project.Documents); AddDocumentSources(project.AdditionalDocuments); @@ -89,7 +90,7 @@ void AddDocumentSources(IEnumerable documents) { // Add the appropriate FSA or CodeAnalysis document source to get document diagnostics. var documentDiagnosticSource = fullSolutionAnalysisEnabled - ? AbstractWorkspaceDocumentDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(document, shouldIncludeAnalyzer) + ? AbstractWorkspaceDocumentDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(document, filter) : AbstractWorkspaceDocumentDiagnosticSource.CreateForCodeAnalysisDiagnostics(document, codeAnalysisService); result.Add(documentDiagnosticSource); } @@ -99,13 +100,10 @@ void AddDocumentSources(IEnumerable documents) void AddProjectSource() { var projectDiagnosticSource = fullSolutionAnalysisEnabled - ? AbstractProjectDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(project, shouldIncludeAnalyzer) + ? AbstractProjectDiagnosticSource.CreateForFullSolutionAnalysisDiagnostics(project, filter) : AbstractProjectDiagnosticSource.CreateForCodeAnalysisDiagnostics(project, codeAnalysisService); result.Add(projectDiagnosticSource); } - - bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer) - => analyzer.IsCompilerAnalyzer() ? compilerFullSolutionAnalysisEnabled : analyzersFullSolutionAnalysisEnabled; } } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs index 30df0450623..fd3cd71ac1f 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractProjectDiagnosticSource.cs @@ -18,8 +18,8 @@ internal abstract class AbstractProjectDiagnosticSource(Project project) protected Project Project => project; protected Solution Solution => this.Project.Solution; - public static AbstractProjectDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(Project project, Func? shouldIncludeAnalyzer) - => new FullSolutionAnalysisDiagnosticSource(project, shouldIncludeAnalyzer); + public static AbstractProjectDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(Project project, AnalyzerFilter analyzerFilter) + => new FullSolutionAnalysisDiagnosticSource(project, analyzerFilter); public static AbstractProjectDiagnosticSource CreateForCodeAnalysisDiagnostics(Project project, ICodeAnalysisDiagnosticAnalyzerService codeAnalysisService) => new CodeAnalysisDiagnosticSource(project, codeAnalysisService); @@ -35,7 +35,7 @@ public static AbstractProjectDiagnosticSource CreateForCodeAnalysisDiagnostics(P public string ToDisplayString() => Project.Name; private sealed class FullSolutionAnalysisDiagnosticSource( - Project project, Func? shouldIncludeAnalyzer) + Project project, AnalyzerFilter analyzerFilter) : AbstractProjectDiagnosticSource(project) { public override async Task> GetDiagnosticsAsync( @@ -47,9 +47,8 @@ public override async Task> GetDiagnosticsAsync( // it will be computed on demand. Because it is always accurate as per this snapshot, all spans are correct // and do not need to be adjusted. var service = this.Solution.Services.GetRequiredService(); - var filter = service.GetDefaultAnalyzerFilter(Project, diagnosticIds: null, shouldIncludeAnalyzer); var diagnostics = await service.GetProjectDiagnosticsForIdsAsync( - Project, diagnosticIds: null, filter, cancellationToken).ConfigureAwait(false); + Project, diagnosticIds: null, analyzerFilter, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): In the future we could consider reporting these, but with a flag on the diagnostic mentioning // that it is suppressed and should be hidden from the task list by default. diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs index a75c275ce26..ec1ac695242 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/AbstractWorkspaceDocumentDiagnosticSource.cs @@ -9,20 +9,21 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal abstract class AbstractWorkspaceDocumentDiagnosticSource(TextDocument document) : AbstractDocumentDiagnosticSource(document) { - public static AbstractWorkspaceDocumentDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(TextDocument document, Func? shouldIncludeAnalyzer) - => new FullSolutionAnalysisDiagnosticSource(document, shouldIncludeAnalyzer); + public static AbstractWorkspaceDocumentDiagnosticSource CreateForFullSolutionAnalysisDiagnostics(TextDocument document, AnalyzerFilter analyzerFilter) + => new FullSolutionAnalysisDiagnosticSource(document, analyzerFilter); public static AbstractWorkspaceDocumentDiagnosticSource CreateForCodeAnalysisDiagnostics(TextDocument document, ICodeAnalysisDiagnosticAnalyzerService codeAnalysisService) => new CodeAnalysisDiagnosticSource(document, codeAnalysisService); private sealed class FullSolutionAnalysisDiagnosticSource( - TextDocument document, Func? shouldIncludeAnalyzer) + TextDocument document, AnalyzerFilter analyzerFilter) : AbstractWorkspaceDocumentDiagnosticSource(document) { /// @@ -38,6 +39,14 @@ public override async Task> GetDiagnosticsAsync( { if (Document is SourceGeneratedDocument sourceGeneratedDocument) { + if (sourceGeneratedDocument.IsRazorSourceGeneratedDocument()) + { + // Razor has not ever participated in diagnostics for closed files, so we filter then out when doing FSA. They will handle + // their own open file diagnostics. Additionally, if we reported them here, they would should up as coming from a `.g.cs` file + // which is not what the user wants anyway. + return []; + } + // Unfortunately GetDiagnosticsForIdsAsync returns nothing for source generated documents. var service = this.Solution.Services.GetRequiredService(); var documentDiagnostics = await service.GetDiagnosticsForSpanAsync( @@ -71,10 +80,8 @@ AsyncLazy> GetLazyDiagnostics() async cancellationToken => { var service = this.Solution.Services.GetRequiredService(); - var filter = service.GetDefaultAnalyzerFilter( - project, diagnosticIds: null, shouldIncludeAnalyzer); var allDiagnostics = await service.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: null, filter, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); + project, documentIds: default, diagnosticIds: null, analyzerFilter, includeLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): Should we be filtering out suppressed diagnostics here? This is how the // code has always worked, but it isn't clear if that is correct. diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs index dbb26f218e7..bbf159bb0bc 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/NonLocalDocumentDiagnosticSource.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal sealed class NonLocalDocumentDiagnosticSource( - TextDocument document, Func? shouldIncludeAnalyzer) + TextDocument document, AnalyzerFilter analyzerFilter) : AbstractDocumentDiagnosticSource(document) { public override async Task> GetDiagnosticsAsync( @@ -22,10 +22,9 @@ public override async Task> GetDiagnosticsAsync( // document including those reported as a compilation end diagnostic. These are not included in document pull // (uses GetDiagnosticsForSpan) due to cost. var service = this.Solution.Services.GetRequiredService(); - var filter = service.GetDefaultAnalyzerFilter(Document.Project, diagnosticIds: null, shouldIncludeAnalyzer); var diagnostics = await service.GetDiagnosticsForIdsAsync( - Document.Project, [Document.Id], diagnosticIds: null, filter, includeLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + Document.Project, [Document.Id], diagnosticIds: null, analyzerFilter, includeLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): In the future we could consider reporting these, but with a flag on the diagnostic mentioning // that it is suppressed and should be hidden from the task list by default. diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs index dd616a40510..aac612e5d8f 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Diagnostics/Public/PublicDocumentNonLocalDiagnosticSourceProvider.cs @@ -33,8 +33,8 @@ public ValueTask> CreateDiagnosticSourcesAsync // Non-local document diagnostics are reported only when full solution analysis is enabled for analyzer execution. if (globalOptions.GetBackgroundAnalysisScope(context.GetRequiredDocument().Project.Language) == BackgroundAnalysisScope.FullSolution) { - // NOTE: Compiler does not report any non-local diagnostics, so we bail out for compiler analyzer. - return new([new NonLocalDocumentDiagnosticSource(context.GetRequiredDocument(), a => !a.IsCompilerAnalyzer())]); + // NOTE: Compiler does not report any non-local diagnostics, so we only ask to run non-compiler-analyzers. + return new([new NonLocalDocumentDiagnosticSource(context.GetRequiredDocument(), AnalyzerFilter.NonCompilerAnalyzer)]); } return new([]); diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs index 41241fe324d..f20d2f77124 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs @@ -28,11 +28,11 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DidChangeTextDocumentPar public Task HandleRequestAsync(DidChangeTextDocumentParams request, RequestContext context, CancellationToken cancellationToken) { - var text = context.GetTrackedDocumentSourceText(request.TextDocument.DocumentUri); + var text = context.GetTrackedDocumentInfo(request.TextDocument.DocumentUri).SourceText; text = GetUpdatedSourceText(request.ContentChanges, text); - context.UpdateTrackedDocument(request.TextDocument.DocumentUri, text); + context.UpdateTrackedDocument(request.TextDocument.DocumentUri, text, request.TextDocument.Version); return SpecializedTasks.Default(); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index 16c05505d5b..14054ddee9c 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -35,6 +35,6 @@ public async Task HandleNotificationAsync(LSP.DidOpenTextDocumentParams request, // Create SourceText from binary representation of the document, retrieve encoding from the request and checksum algorithm from the project. var sourceText = SourceText.From(request.TextDocument.Text, System.Text.Encoding.UTF8, SourceHashAlgorithms.OpenDocumentChecksumAlgorithm); - await context.StartTrackingAsync(request.TextDocument.DocumentUri, sourceText, request.TextDocument.LanguageId, cancellationToken).ConfigureAwait(false); + await context.StartTrackingAsync(request.TextDocument.DocumentUri, sourceText, request.TextDocument.LanguageId, request.TextDocument.Version, cancellationToken).ConfigureAwait(false); } } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 1e06d5420aa..2237fb10798 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; @@ -12,6 +14,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -48,14 +51,27 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) return []; } - var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); + var linePosition = ProtocolConversions.PositionToLinePosition(request.Position); + var position = await document.GetPositionFromLinePositionAsync(linePosition, cancellationToken).ConfigureAwait(false); var formattingService = document.Project.Services.GetRequiredService(); var documentSyntax = await ParsedDocument.CreateAsync(document, cancellationToken).ConfigureAwait(false); - - if (!formattingService.ShouldFormatOnTypedCharacter(documentSyntax, request.Character[0], position, cancellationToken)) + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + // The formatting service expects that the position is inside the token span associated with the typed character, but + // in VSCode this is not always the case - the position the client gives us is not necessarily the position of the typed character. + // For example when typing characters, the client may automatically + // 1. for '\n' - the client inserts indentation (to get '\n ') + // 2. for '{' - the client inserts '}' (to get '{}') + // + // When the formatter calls root.FindToken, it may return a token different from what triggered the on type formatting, which causes us + // to format way more than we want, depending on exactly where the position ends up. + // Here we do our best to adjust the position back to the typed char location. + if (text[position - 1] != request.Character[0]) { - return []; + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var adjustedToken = root.FindTokenOnLeftOfPosition(position); + position = adjustedToken.Span.End; } // We should use the options passed in by LSP instead of the document's options. @@ -65,41 +81,15 @@ public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) AutoFormattingOptions = _globalOptions.GetAutoFormattingOptions(document.Project.Language) }; - var textChanges = formattingService.GetFormattingChangesOnTypedCharacter(documentSyntax, position, indentationOptions, cancellationToken); - if (textChanges.IsEmpty) + if (!formattingService.ShouldFormatOnTypedCharacter(documentSyntax, request.Character[0], position, cancellationToken)) { return []; } - if (SyntaxFacts.IsNewLine(request.Character[0])) + var textChanges = formattingService.GetFormattingChangesOnTypedCharacter(documentSyntax, position, indentationOptions, cancellationToken); + if (textChanges.IsEmpty) { - // When formatting after a newline is pressed, the cursor line will be all whitespace - // and we do not want to remove the indentation from it. - // - // Take the following example of pressing enter after an opening brace. - // - // ``` - // public void M() {||} - // ``` - // - // The editor moves the cursor to the next line and uses it's languageconfig to add - // the appropriate level of indentation. - // - // ``` - // public void M() { - // || - // } - // ``` - // - // At this point `formatOnType` is called. The formatting service will generate two - // text changes. The first moves the opening brace to a new line with proper - // indentation. The second removes the whitespace from the cursor line and rewrites - // the indentation prior to the closing brace. - // - // Letting the second change go through would be a bad experience for the user as they - // will now be responsible for adding back the proper indentation. - - textChanges = textChanges.WhereAsArray(static (change, position) => !change.Span.Contains(position), position); + return []; } return [.. textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, documentSyntax.Text))]; diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs index 33f71a36000..bce1d95bb73 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs @@ -17,14 +17,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; /// internal interface IDocumentChangeTracker { - ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, CancellationToken cancellationToken); - void UpdateTrackedDocument(DocumentUri documentUri, SourceText text); + ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken); + void UpdateTrackedDocument(DocumentUri documentUri, SourceText text, int lspVersion); ValueTask StopTrackingAsync(DocumentUri documentUri, CancellationToken cancellationToken); } internal sealed class NonMutatingDocumentChangeTracker : IDocumentChangeTracker { - public ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, CancellationToken cancellationToken) + public ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken) { throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler"); } @@ -34,7 +34,7 @@ public ValueTask StopTrackingAsync(DocumentUri documentUri, CancellationToken ca throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler"); } - public void UpdateTrackedDocument(DocumentUri documentUri, SourceText text) + public void UpdateTrackedDocument(DocumentUri documentUri, SourceText text, int lspVersion) { throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler"); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index fdc6f3c6eb9..0214c5f21cc 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -49,14 +49,19 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) internal static async Task GetInlayHintsAsync(Document document, TextDocumentIdentifier textDocumentIdentifier, LSP.Range range, InlineHintsOptions options, bool displayAllOverride, InlayHintCache inlayHintCache, CancellationToken cancellationToken) { - var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var hints = await CalculateInlayHintsAsync(document, range, options, displayAllOverride, cancellationToken).ConfigureAwait(false); - var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); // Store the members in the resolve cache so that when we get a resolve request for a particular // member we can re-use the inline hint. var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints)); + if (hints.Length == 0) + return Array.Empty(); + + var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); + var syntaxVersionString = syntaxVersion.ToString(); + var inlayHints = new LSP.InlayHint[hints.Length]; for (var i = 0; i < hints.Length; i++) { @@ -85,7 +90,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) ToolTip = null, PaddingLeft = leftPadding, PaddingRight = rightPadding, - Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier, syntaxVersion.ToString(), range, displayAllOverride) + Data = new InlayHintResolveData(resultId, i, textDocumentIdentifier, syntaxVersionString, range, displayAllOverride) }; inlayHints[i] = inlayHint; @@ -110,35 +115,34 @@ internal static async Task> CalculateInlayHintsAsync( /// private static (string label, bool leftPadding, bool rightPadding) Trim(ImmutableArray taggedTexts) { - using var _ = ArrayBuilder.GetInstance(out var result); var leftPadding = false; var rightPadding = false; + if (taggedTexts.Length == 0) + return (string.Empty, leftPadding, rightPadding); + + var first = taggedTexts.First(); + var trimStart = first.Text.TrimStart(); + leftPadding = first.Text.Length - trimStart.Length != 0; + if (taggedTexts.Length == 1) { - var first = taggedTexts.First(); - - var trimStart = first.Text.TrimStart(); var trimBoth = trimStart.TrimEnd(); - result.Add(new TaggedText(first.Tag, trimBoth)); - leftPadding = first.Text.Length - trimStart.Length != 0; rightPadding = trimStart.Length - trimBoth.Length != 0; + + return (trimBoth, leftPadding, rightPadding); } - else if (taggedTexts.Length >= 2) - { - var first = taggedTexts.First(); - var trimStart = first.Text.TrimStart(); - result.Add(new TaggedText(first.Tag, trimStart)); - leftPadding = first.Text.Length - trimStart.Length != 0; - - for (var i = 1; i < taggedTexts.Length - 1; i++) - result.Add(taggedTexts[i]); - - var last = taggedTexts.Last(); - var trimEnd = last.Text.TrimEnd(); - result.Add(new TaggedText(last.Tag, trimEnd)); - rightPadding = last.Text.Length - trimEnd.Length != 0; - } + + using var _ = ArrayBuilder.GetInstance(out var result); + result.Add(new TaggedText(first.Tag, trimStart)); + + for (var i = 1; i < taggedTexts.Length - 1; i++) + result.Add(taggedTexts[i]); + + var last = taggedTexts.Last(); + var trimEnd = last.Text.TrimEnd(); + result.Add(new TaggedText(last.Tag, trimEnd)); + rightPadding = last.Text.Length - trimEnd.Length != 0; return (result.ToImmutable().JoinText(), leftPadding, rightPadding); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs index 0ca5cd43d7e..cb5db1ae695 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/InlineCompletions/XmlSnippetParser.cs @@ -100,7 +100,7 @@ private static CodeSnippet RetrieveSnippetXmlFromFile(SnippetInfo snippetInfo) return snippet; } - internal TestAccessor GetTestAccessor() => new TestAccessor(this); + internal TestAccessor GetTestAccessor() => new(this); internal readonly struct TestAccessor { diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index cd6c950489a..d46f2b9f3c5 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -57,7 +57,12 @@ internal sealed class OnAutoInsertHandler( var servicesForDocument = _braceCompletionServices.SelectAsArray(s => s.Metadata.Language == document.Project.Language, s => s.Value); var isRazorRequest = context.ServerKind == WellKnownLspServerKinds.RazorLspServer; var position = ProtocolConversions.PositionToLinePosition(request.Position); - return GetOnAutoInsertResponseAsync(_globalOptions, servicesForDocument, document, position, request.Character, request.Options, isRazorRequest, cancellationToken); + var supportsVSExtensions = context.GetRequiredClientCapabilities().HasVisualStudioLspCapability(); + + // We want adjust the braces after enter for razor and non-VS clients. + // We don't do this via on type formatting as it does not support snippets. + var includeNewLineBraceFormatting = isRazorRequest || !supportsVSExtensions; + return GetOnAutoInsertResponseAsync(_globalOptions, servicesForDocument, document, position, request.Character, request.Options, includeNewLineBraceFormatting, cancellationToken); } internal static async Task GetOnAutoInsertResponseAsync( @@ -67,7 +72,7 @@ internal sealed class OnAutoInsertHandler( LinePosition linePosition, string character, LSP.FormattingOptions lspFormattingOptions, - bool isRazorRequest, + bool includeNewLineBraceFormatting, CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); @@ -92,7 +97,8 @@ internal sealed class OnAutoInsertHandler( // Only support this for razor as LSP doesn't support overtype yet. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1165179/ // Once LSP supports overtype we can move all of brace completion to LSP. - if (character == "\n" && isRazorRequest) + + if (character == "\n" && includeNewLineBraceFormatting) { var indentationOptions = new IndentationOptions(formattingOptions) { @@ -125,7 +131,7 @@ internal sealed class OnAutoInsertHandler( var result = character == "\n" ? service.GetDocumentationCommentSnippetOnEnterTyped(parsedDocument, position, options, cancellationToken) - : service.GetDocumentationCommentSnippetOnCharacterTyped(parsedDocument, position, options, cancellationToken, addIndentation: false); + : service.GetDocumentationCommentSnippetOnCharacterTyped(parsedDocument, position, options, cancellationToken, addIndentation: true); if (result == null) return null; @@ -194,6 +200,7 @@ internal sealed class OnAutoInsertHandler( var textChange = await GetCollapsedChangeAsync(textChanges, document, cancellationToken).ConfigureAwait(false); var newText = GetTextChangeTextWithCaretAtLocation(newSourceText, textChange, desiredCaretLinePosition); + var autoInsertChange = new LSP.VSInternalDocumentOnAutoInsertResponseItem { TextEditFormat = LSP.InsertTextFormat.Snippet, diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs index 0f718729323..62fb88c9768 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs @@ -24,7 +24,7 @@ internal class GetTextDocumentWithContextHandler() : ILspServiceDocumentRequestH public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(VSGetProjectContextsParams request) => new TextDocumentIdentifier { DocumentUri = request.TextDocument.DocumentUri }; + public TextDocumentIdentifier GetTextDocumentIdentifier(VSGetProjectContextsParams request) => new() { DocumentUri = request.TextDocument.DocumentUri }; public Task HandleRequestAsync(VSGetProjectContextsParams request, RequestContext context, CancellationToken cancellationToken) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs b/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs index c5639e2e425..02ffa3696df 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Handler/RequestContext.cs @@ -42,7 +42,7 @@ internal readonly struct RequestContext /// It contains text that is consistent with all prior LSP text sync notifications, but LSP text sync requests /// which are ordered after this one in the queue are not reflected here. /// - private readonly ImmutableDictionary _trackedDocuments; + private readonly ImmutableDictionary _trackedDocuments; private readonly ILspServices _lspServices; @@ -171,7 +171,7 @@ public RequestContext( WellKnownLspServerKinds serverKind, TextDocument? document, IDocumentChangeTracker documentChangeTracker, - ImmutableDictionary trackedDocuments, + ImmutableDictionary trackedDocuments, ImmutableArray supportedLanguages, ILspServices lspServices, CancellationToken queueCancellationToken) @@ -299,20 +299,20 @@ public static async Task CreateAsync( /// Allows a mutating request to open a document and start it being tracked. /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. /// - public ValueTask StartTrackingAsync(DocumentUri uri, SourceText initialText, string languageId, CancellationToken cancellationToken) - => _documentChangeTracker.StartTrackingAsync(uri, initialText, languageId, cancellationToken); + public ValueTask StartTrackingAsync(DocumentUri uri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken) + => _documentChangeTracker.StartTrackingAsync(uri, initialText, languageId, lspVersion, cancellationToken); /// /// Allows a mutating request to update the contents of a tracked document. /// Mutating requests are serialized by the execution queue in order to prevent concurrent access. /// - public void UpdateTrackedDocument(DocumentUri uri, SourceText changedText) - => _documentChangeTracker.UpdateTrackedDocument(uri, changedText); + public void UpdateTrackedDocument(DocumentUri uri, SourceText changedText, int lspVersion) + => _documentChangeTracker.UpdateTrackedDocument(uri, changedText, lspVersion); - public SourceText GetTrackedDocumentSourceText(DocumentUri documentUri) + public TrackedDocumentInfo GetTrackedDocumentInfo(DocumentUri documentUri) { Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(documentUri), $"Attempted to get text for {documentUri} which is not open."); - return _trackedDocuments[documentUri].Text; + return _trackedDocuments[documentUri]; } /// diff --git a/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs b/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs index ef4b6a5db2c..dcd3db3cde0 100644 --- a/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs +++ b/src/roslyn/src/LanguageServer/Protocol/LanguageInfoProvider.cs @@ -18,7 +18,7 @@ internal sealed class LanguageInfoProvider : ILanguageInfoProvider private static readonly LanguageInformation s_csharpLanguageInformation = new(LanguageNames.CSharp, ".csx"); private static readonly LanguageInformation s_fsharpLanguageInformation = new(LanguageNames.FSharp, ".fsx"); private static readonly LanguageInformation s_vbLanguageInformation = new(LanguageNames.VisualBasic, ".vbx"); - private static readonly LanguageInformation s_typeScriptLanguageInformation = new LanguageInformation(InternalLanguageNames.TypeScript, scriptExtension: null); + private static readonly LanguageInformation s_typeScriptLanguageInformation = new(InternalLanguageNames.TypeScript, scriptExtension: null); private static readonly LanguageInformation s_razorLanguageInformation = new(RazorLanguageName, scriptExtension: null); private static readonly LanguageInformation s_xamlLanguageInformation = new("XAML", scriptExtension: null); diff --git a/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs b/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs index 13b951a853f..1b76529a669 100644 --- a/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs +++ b/src/roslyn/src/LanguageServer/Protocol/NoOpLspLogger.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer; internal sealed class NoOpLspLogger : AbstractLspLogger, ILspService { - public static readonly NoOpLspLogger Instance = new NoOpLspLogger(); + public static readonly NoOpLspLogger Instance = new(); private NoOpLspLogger() { } diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs index 8c766764a6f..a2837a9a0e1 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs @@ -182,7 +182,7 @@ public UnionTypeInfo(Type type, ConstructorInfo constructor, KindAttribute? kind internal sealed class SumConverter : JsonConverter where T : struct, ISumType { - private static readonly ConcurrentDictionary SumTypeCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary SumTypeCache = new(); /// public override T Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs index 2c36060f63d..0345451d051 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Extensions/VSMethods.cs @@ -23,5 +23,5 @@ internal static class VSMethods /// /// Strongly typed request object for 'textDocument/_vs_getProjectContexts'. /// - public static readonly LspRequest GetProjectContexts = new LspRequest(GetProjectContextsName); + public static readonly LspRequest GetProjectContexts = new(GetProjectContextsName); } diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs index 09e0bb5b763..2708b28c4e3 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs @@ -14,7 +14,7 @@ namespace Roslyn.LanguageServer.Protocol; internal sealed class OptimizedVSCompletionListJsonConverter : JsonConverter { public static readonly OptimizedVSCompletionListJsonConverter Instance = new(); - private static readonly ConcurrentDictionary IconRawJson = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary IconRawJson = new(); public override OptimizedVSCompletionList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs index d3ac3b36659..05c4a21bca6 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/Internal/VSInternalMethods.cs @@ -28,52 +28,52 @@ internal static class VSInternalMethods /// /// Strongly typed message object for 'textDocument/_vs_onAutoInsert'. /// - public static readonly LspRequest OnAutoInsert = new LspRequest(OnAutoInsertName); + public static readonly LspRequest OnAutoInsert = new(OnAutoInsertName); /// /// Strongly typed message object for 'textDocument/_vs_iconMappingResolve'. /// - public static readonly LspRequest TextDocumentIconMappingResolve = new LspRequest(TextDocumentIconMappingResolveName); + public static readonly LspRequest TextDocumentIconMappingResolve = new(TextDocumentIconMappingResolveName); /// /// Strongly typed message object for 'textDocument/_vs_diagnostic'. /// - public static readonly LspRequest DocumentPullDiagnostic = new LspRequest(DocumentPullDiagnosticName); + public static readonly LspRequest DocumentPullDiagnostic = new(DocumentPullDiagnosticName); /// /// Strongly typed message object for 'workspace/_vs_diagnostic'. /// - public static readonly LspRequest WorkspacePullDiagnostic = new LspRequest(WorkspacePullDiagnosticName); + public static readonly LspRequest WorkspacePullDiagnostic = new(WorkspacePullDiagnosticName); /// /// Strongly typed message object for 'textDocument/_vs_validateBreakableRange'. /// - public static readonly LspRequest TextDocumentValidateBreakableRange = new LspRequest(TextDocumentValidateBreakableRangeName); + public static readonly LspRequest TextDocumentValidateBreakableRange = new(TextDocumentValidateBreakableRangeName); /// /// Strongly typed message object for 'textDocument/inlineCompletion'. /// - public static readonly LspRequest TextDocumentInlineCompletion = new LspRequest(TextDocumentInlineCompletionName); + public static readonly LspRequest TextDocumentInlineCompletion = new(TextDocumentInlineCompletionName); /// /// Strongly typed message object for 'textDocument/_vs_uriPresentation'. /// - public static readonly LspRequest TextDocumentUriPresentation = new LspRequest(TextDocumentUriPresentationName); + public static readonly LspRequest TextDocumentUriPresentation = new(TextDocumentUriPresentationName); /// /// Strongly typed message object for 'textDocument/_vs_textPresentation'. /// - public static readonly LspRequest TextDocumentTextPresentation = new LspRequest(TextDocumentTextPresentationName); + public static readonly LspRequest TextDocumentTextPresentation = new(TextDocumentTextPresentationName); /// /// Strongly typed message object for 'textDocument/_vs_spellCheckableRanges'. /// - public static readonly LspRequest TextDocumentSpellCheckableRanges = new LspRequest(TextDocumentSpellCheckableRangesName); + public static readonly LspRequest TextDocumentSpellCheckableRanges = new(TextDocumentSpellCheckableRangesName); /// /// Strongly typed message object for 'workspace/_vs_spellCheckableRanges'. /// - public static readonly LspRequest WorkspaceSpellCheckableRanges = new LspRequest(WorkspaceSpellCheckableRangesName); + public static readonly LspRequest WorkspaceSpellCheckableRanges = new(WorkspaceSpellCheckableRangesName); /// /// Strongly typed message object for 'workspace/_vs_mapCode' diff --git a/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs b/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs index 1dc5b878faa..c41306f918f 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Protocol/SumType.cs @@ -62,7 +62,7 @@ public SumType(T2 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrapped. - public static implicit operator SumType(T1 val) => new SumType(val); + public static implicit operator SumType(T1 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -74,7 +74,7 @@ public SumType(T2 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrapped. - public static implicit operator SumType(T2 val) => new SumType(val); + public static implicit operator SumType(T2 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -270,7 +270,7 @@ public SumType(T3 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T1 val) => new SumType(val); + public static implicit operator SumType(T1 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -282,7 +282,7 @@ public SumType(T3 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T2 val) => new SumType(val); + public static implicit operator SumType(T2 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -294,7 +294,7 @@ public SumType(T3 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T3 val) => new SumType(val); + public static implicit operator SumType(T3 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -570,7 +570,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T1 val) => new SumType(val); + public static implicit operator SumType(T1 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -582,7 +582,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T2 val) => new SumType(val); + public static implicit operator SumType(T2 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -594,7 +594,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T3 val) => new SumType(val); + public static implicit operator SumType(T3 val) => new(val); /// /// Implicitly wraps a value of type with a . @@ -606,7 +606,7 @@ public SumType(T4 val) /// Implicitly wraps a value of type with a . /// /// Value to be wrap. - public static implicit operator SumType(T4 val) => new SumType(val); + public static implicit operator SumType(T4 val) => new(val); /// /// Implicitly wraps a value of type with a . diff --git a/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index 832fb857a15..329d1f15c6b 100644 --- a/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/roslyn/src/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -62,7 +62,7 @@ internal sealed class LspWorkspaceManager : IDocumentChangeTracker, ILspService /// the URI. /// Access to this is guaranteed to be serial by the /// - private ImmutableDictionary _trackedDocuments = ImmutableDictionary.Empty; + private ImmutableDictionary _trackedDocuments = ImmutableDictionary.Empty; private readonly ILspLogger _logger; private readonly ILspMiscellaneousFilesWorkspaceProvider? _lspMiscellaneousFilesWorkspaceProvider; @@ -103,7 +103,7 @@ private static async ValueTask ApplyChangeToMutatingWorkspaceAsync(Workspace wor /// /// is true which means this runs serially in the /// - public async ValueTask StartTrackingAsync(DocumentUri uri, SourceText documentText, string languageId, CancellationToken cancellationToken) + public async ValueTask StartTrackingAsync(DocumentUri uri, SourceText documentText, string languageId, int lspVersion, CancellationToken cancellationToken) { // First, store the LSP view of the text as the uri is now owned by the LSP client. Contract.ThrowIfTrue(_trackedDocuments.ContainsKey(uri), $"didOpen received for {uri} which is already open."); @@ -113,7 +113,7 @@ public async ValueTask StartTrackingAsync(DocumentUri uri, SourceText documentTe _logger.LogError($"Unable to parse URI {uri}"); } - _trackedDocuments = _trackedDocuments.Add(uri, (documentText, languageId)); + _trackedDocuments = _trackedDocuments.Add(uri, new(documentText, languageId, lspVersion)); // If LSP changed, we need to compare against the workspace again to get the updated solution. _cachedLspSolutions.Clear(); @@ -197,12 +197,12 @@ await ApplyChangeToMutatingWorkspaceAsync(workspace, uri, async (_, documentId) /// /// is true which means this runs serially in the /// - public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) + public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText, int lspVersion) { // Store the updated LSP view of the source text. Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(uri), $"didChange received for {uri} which is not open."); - var (_, language) = _trackedDocuments[uri]; - _trackedDocuments = _trackedDocuments.SetItem(uri, (newSourceText, language)); + var (_, language, _) = _trackedDocuments[uri]; + _trackedDocuments = _trackedDocuments.SetItem(uri, new(newSourceText, language, lspVersion)); // If LSP changed, we need to compare against the workspace again to get the updated solution. _cachedLspSolutions.Clear(); @@ -210,7 +210,7 @@ public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) LspTextChanged?.Invoke(this, EventArgs.Empty); } - public ImmutableDictionary GetTrackedLspText() => _trackedDocuments; + public ImmutableDictionary GetTrackedLspText() => _trackedDocuments; #endregion @@ -285,7 +285,7 @@ public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) var workspaceKind = document.Project.Solution.WorkspaceKind; _requestTelemetryLogger.UpdateFindDocumentTelemetryData(success: true, workspaceKind); _requestTelemetryLogger.UpdateUsedForkedSolutionCounter(isForked); - _logger.LogDebug($"{document.FilePath} found in workspace {workspaceKind}"); + _logger.LogDebug($"{document.FilePath} found in workspace {workspaceKind}; project {document.Project.Name}"); return (workspace, document.Project.Solution, document); } @@ -302,7 +302,7 @@ public void UpdateTrackedDocument(DocumentUri uri, SourceText newSourceText) { try { - var miscDocument = await _lspMiscellaneousFilesWorkspaceProvider.AddMiscellaneousDocumentAsync(uri, trackedDocument.Text, trackedDocument.LanguageId, _logger).ConfigureAwait(false); + var miscDocument = await _lspMiscellaneousFilesWorkspaceProvider.AddMiscellaneousDocumentAsync(uri, trackedDocument.SourceText, trackedDocument.LanguageId, _logger).ConfigureAwait(false); if (miscDocument is not null) return (miscDocument.Project.Solution.Workspace, miscDocument.Project.Solution, miscDocument); } @@ -391,10 +391,10 @@ .. registeredWorkspaces.Where(workspace => workspace.Kind == WorkspaceKind.Misce var sourceGeneratedDocuments = _trackedDocuments.Keys.Where(static trackedDocument => trackedDocument.ParsedUri?.Scheme == SourceGeneratedDocumentUri.Scheme) // We know we have a non null URI with a source generated scheme. - .Select(uri => (identity: SourceGeneratedDocumentUri.DeserializeIdentity(workspaceCurrentSolution, uri.ParsedUri!), _trackedDocuments[uri].Text)) + .Select(uri => (identity: SourceGeneratedDocumentUri.DeserializeIdentity(workspaceCurrentSolution, uri.ParsedUri!), _trackedDocuments[uri].SourceText)) .SelectAsArray( predicate: tuple => tuple.identity.HasValue, - selector: tuple => (tuple.identity!.Value, DateTime.Now, tuple.Text)); + selector: tuple => (tuple.identity!.Value, DateTime.Now, tuple.SourceText)); // First we check if normal document text matches the workspace solution. // This does not look at source generated documents. @@ -428,7 +428,7 @@ .. registeredWorkspaces.Where(workspace => workspace.Kind == WorkspaceKind.Misce if (!doesAllTextMatch) { foreach (var (uri, workspaceDocuments) in documentsInWorkspace) - lspSolution = lspSolution.WithDocumentText(workspaceDocuments.Select(d => d.Id), _trackedDocuments[uri].Text); + lspSolution = lspSolution.WithDocumentText(workspaceDocuments.Select(d => d.Id), _trackedDocuments[uri].SourceText); } // If the source generated documents matched we can leave the source generated documents as-is @@ -444,7 +444,7 @@ .. registeredWorkspaces.Where(workspace => workspace.Kind == WorkspaceKind.Misce async ValueTask TryOpenAndEditDocumentsInMutatingWorkspaceAsync(Workspace workspace) { - foreach (var (uri, (sourceText, _)) in _trackedDocuments) + foreach (var (uri, (sourceText, _, _)) in _trackedDocuments) { await ApplyChangeToMutatingWorkspaceAsync(workspace, uri, async (mutatingWorkspace, documentId) => { @@ -512,7 +512,7 @@ private async Task DoesAllTextMatchWorkspaceSolutionAsync(ImmutableDiction { // We're comparing text, so we can take any of the linked documents. var firstDocument = documentsForUri.First(); - var isTextEquivalent = await AreChecksumsEqualAsync(firstDocument, _trackedDocuments[uriInWorkspace].Text, cancellationToken).ConfigureAwait(false); + var isTextEquivalent = await AreChecksumsEqualAsync(firstDocument, _trackedDocuments[uriInWorkspace].SourceText, cancellationToken).ConfigureAwait(false); if (!isTextEquivalent) { diff --git a/src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.cs b/src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.cs new file mode 100644 index 00000000000..ff9d2a2b86a --- /dev/null +++ b/src/roslyn/src/LanguageServer/Protocol/Workspaces/TrackedDocumentInfo.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 Microsoft.CodeAnalysis.Text; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer; + +/// +/// Holds state information from the client about a tracked LSP document. See +/// +/// A snapshot of the text as seen by LSP. +/// The language id for the text, from +/// The client version associated with the text, from +internal record struct TrackedDocumentInfo(SourceText SourceText, string LanguageId, int LspVersion); diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs index b2d1efa5074..98a57c91210 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs @@ -521,7 +521,7 @@ class BCD } private static LSP.TextEdit GenerateTextEdit(string newText, LSP.Range range) - => new LSP.TextEdit + => new() { NewText = newText, Range = range @@ -530,11 +530,11 @@ private static LSP.TextEdit GenerateTextEdit(string newText, LSP.Range range) private static WorkspaceEdit GenerateWorkspaceEdit( IList locations, SumType[] edits) - => new LSP.WorkspaceEdit + => new() { DocumentChanges = new TextDocumentEdit[] { - new TextDocumentEdit + new() { TextDocument = new OptionalVersionedTextDocumentIdentifier { diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index 6f38e8f3557..639deea8e43 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -138,43 +138,6 @@ void M() Assert.Equal(AddImportDiagnosticIds.CS0103, addImport.Diagnostics.Single().Code!.Value); } - [Theory, CombinatorialData] - public async Task TestNoSuppressionFixerInStandardLSP(bool mutatingLspWorkspace) - { - var markup = """ - class ABC - { - private static async void {|caret:XYZ|}() - { - } - } - """; - - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - - var caret = testLspServer.GetLocations("caret").Single(); - var codeActionParams = new CodeActionParams - { - TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), - Range = caret.Range, - Context = new CodeActionContext - { - Diagnostics = - [ - new LSP.Diagnostic - { - // async method lack of await. - Code = "CS1998" - } - ] - } - }; - - var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); - Assert.Equal(3, results.Length); - Assert.Equal("Make method synchronous", results[0].Title); - } - [Theory, CombinatorialData] public async Task TestStandardLspNestedCodeAction(bool mutatingLspWorkspace) { @@ -227,8 +190,9 @@ public async Task TestStandardLspNestedFixAllCodeAction(bool mutatingLspWorkspac var markup = """ class ABC { - private static async void {|caret:XYZ|}() + private static async void XYZ() { + {|caret:System.Threading.Tasks.Task.Yield()|}; } } """; @@ -246,17 +210,17 @@ class ABC [ new LSP.Diagnostic { - // async method lack of await. - Code = "CS1998" + // Task-returning method not awaited + Code = "CS4014" } ] } }; var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); - Assert.Equal(3, results.Length); - Assert.Equal("Suppress or configure issues", results[2].Title); - var data = GetCodeActionResolveData(results[2]); + Assert.Equal(10, results.Length); + Assert.Equal("Suppress or configure issues", results[9].Title); + var data = GetCodeActionResolveData(results[9]); Assert.NotNull(data); // Asserts that there are NestedActions present @@ -325,7 +289,7 @@ private static async Task RunGetCodeActionResolveAsync( } internal static CodeActionParams CreateCodeActionParams(LSP.Location caret) - => new CodeActionParams + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), Range = caret.Range, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index 82ac970ee9e..f3cb0980f92 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -184,7 +184,7 @@ private static TestDiagnosticResult ConvertWorkspaceDiagnosticResult(SumType CreateDiagnosticParamsFromPreviousReports(ImmutableArray results) { // If there was no resultId provided in the response, we cannot create previous results for it. - return [.. results.Where(r => r.ResultId != null).Select(r => (r.ResultId!, r.TextDocument))]; + return results.SelectAsArray(r => r.ResultId != null, r => (r.ResultId!, r.TextDocument)); } private protected static VSInternalDocumentDiagnosticsParams CreateDocumentDiagnosticParams( @@ -371,8 +371,8 @@ private protected sealed record TestDiagnosticResult(TextDocumentIdentifier Text [DiagnosticAnalyzer(InternalLanguageNames.TypeScript), PartNotDiscoverable] private sealed class MockTypescriptDiagnosticAnalyzer : DocumentDiagnosticAnalyzer { - public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor( - "TS01", "TS error", "TS error", "Error", DiagnosticSeverity.Error, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor Descriptor = new( + "TS01", "TS error", "TS error", "Error", DiagnosticSeverity.Error, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor]; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs index 35adfcd8398..da3f4b8f745 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs @@ -246,7 +246,7 @@ public Task> GetDiagnosticsAsync(RequestContext c return Task.FromResult>([DiagnosticData.Create(diagnostic, context.TextDocument.Project)]); } - public LSP.TextDocumentIdentifier? GetDocumentIdentifier() => new LSP.TextDocumentIdentifier + public LSP.TextDocumentIdentifier? GetDocumentIdentifier() => new() { DocumentUri = textDocument.GetURI() }; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs index e3a9aa893d2..e8fdc899bc9 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Diagnostics/WorkspaceProjectDiagnosticsTests.cs @@ -62,6 +62,30 @@ public async Task TestWorkspaceDiagnosticsWithRemovedProject(bool useVSDiagnosti Assert.Null(results2[1].ResultId); } + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsWithRazorSourceGeneratedDocument(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(string.Empty, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + + var razorGenerator = new Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator((c) => c.AddSource("generated_file.cs", "this C# file has syntax errors")); + var workspace = testLspServer.TestWorkspace; + var project = workspace.CurrentSolution.Projects.First().AddAnalyzerReference(new TestGeneratorReference(razorGenerator)); + workspace.TryApplyChanges(project.Solution); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + + var generatedDocument = (await testLspServer.GetCurrentSolution().Projects.First().GetSourceGeneratedDocumentsAsync()).First(); + + // Make sure the test is valid + Assert.Equal(3, results.Length); + Assert.Equal(MockProjectDiagnosticAnalyzer.Id, results[2].Diagnostics!.Single().Code); + Assert.Equal(ProtocolConversions.CreateAbsoluteDocumentUri(testLspServer.GetCurrentSolution().Projects.First().FilePath!), results[2].Uri); + + // Generated document should have no diagnostics + Assert.Equal(generatedDocument.GetURI(), results[1].Uri); + AssertEx.Empty(results[1].Diagnostics); + } + protected override TestComposition Composition => base.Composition.AddParts(typeof(MockProjectDiagnosticAnalyzer)); private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs index 364dec55b72..edb76dd49fa 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs @@ -2,9 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Composition; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; @@ -13,11 +20,9 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.DocumentChanges; -public sealed partial class DocumentChangesTests : AbstractLanguageServerProtocolTests +public sealed partial class DocumentChangesTests(ITestOutputHelper testOutputHelper) : AbstractLanguageServerProtocolTests(testOutputHelper) { - public DocumentChangesTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - } + protected override TestComposition Composition => base.Composition.AddParts(typeof(TestVersionHandler)); [Theory, CombinatorialData] public async Task DocumentChanges_EndToEnd(bool mutatingLspWorkspace) @@ -425,6 +430,33 @@ void M() } } + [Theory, CombinatorialData] + public async Task DocumentChanges_WithVersion(bool mutatingLspWorkspace) + { + await using var testLspServer = await CreateTestLspServerAsync("Hello{|type:|}", mutatingLspWorkspace, CapabilitiesWithVSExtensions); + Assert.Empty(testLspServer.GetTrackedTexts()); + + var locationTyped = testLspServer.GetLocations("type").Single(); + + await DidOpen(testLspServer, locationTyped.DocumentUri, version: 0); + var version = await GetVersionAsync(locationTyped.DocumentUri); + Assert.Equal(0, version); + + await DidChange(testLspServer, locationTyped.DocumentUri, version: 1, (0, 5, ", World")); + Assert.Equal(1, await GetVersionAsync(locationTyped.DocumentUri)); + + var document = testLspServer.GetTrackedTexts().FirstOrDefault(); + Assert.Equal("Hello, World", document!.ToString()); + + async Task GetVersionAsync(DocumentUri documentUri) + { + var textDocumentIdentifier = new LSP.TextDocumentIdentifier() { DocumentUri = documentUri }; + var response = await testLspServer.ExecuteRequestAsync(TestVersionHandler.MethodName, textDocumentIdentifier, CancellationToken.None); + Assert.NotNull(response); + return response.Version; + } + } + private async Task<(TestLspServer, LSP.Location, string)> GetTestLspServerAndLocationAsync(string source, bool mutatingLspWorkspace) { var testLspServer = await CreateTestLspServerAsync(source, mutatingLspWorkspace, CapabilitiesWithVSExtensions); @@ -434,10 +466,39 @@ void M() return (testLspServer, locationTyped, documentText.ToString()); } - private static Task DidOpen(TestLspServer testLspServer, DocumentUri uri) => testLspServer.OpenDocumentAsync(uri); + private static Task DidOpen(TestLspServer testLspServer, DocumentUri uri, int version = 0) => testLspServer.OpenDocumentAsync(uri, version: version); - private static async Task DidChange(TestLspServer testLspServer, DocumentUri uri, params (int line, int column, string text)[] changes) - => await testLspServer.InsertTextAsync(uri, changes); + private static async Task DidChange(TestLspServer testLspServer, DocumentUri uri, int version, params (int line, int column, string text)[] changes) + => await testLspServer.InsertTextAsync(uri, version, changes); + + private static Task DidChange(TestLspServer testLspServer, DocumentUri uri, params (int line, int column, string text)[] changes) + => DidChange(testLspServer, uri, version: 0, changes); private static async Task DidClose(TestLspServer testLspServer, DocumentUri uri) => await testLspServer.CloseDocumentAsync(uri); + + internal record TestVersionResponse(int Version); + + [ExportCSharpVisualBasicStatelessLspService(typeof(TestVersionHandler)), PartNotDiscoverable, Shared] + [Method(MethodName)] + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class TestVersionHandler() : ILspServiceDocumentRequestHandler + { + public const string MethodName = nameof(TestVersionHandler); + + public bool MutatesSolutionState => false; + public bool RequiresLSPSolution => true; + + public TextDocumentIdentifier GetTextDocumentIdentifier(TextDocumentIdentifier request) + { + return request; + } + + public Task HandleRequestAsync(TextDocumentIdentifier request, RequestContext context, CancellationToken cancellationToken) + { + var trackedDocumentInfo = context.GetTrackedDocumentInfo(request.DocumentUri); + + return Task.FromResult(new TestVersionResponse(trackedDocumentInfo.LspVersion)); + } + } } diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs index 5b0de3bd09d..78e7aa8133d 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs @@ -90,7 +90,7 @@ private async Task AssertFoldingRanges(bool mutatingLspWorkspace, string markup, } private static LSP.FoldingRange CreateFoldingRange(string kind, LSP.Range range, string collapsedText) - => new LSP.FoldingRange() + => new() { Kind = kind switch { diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs index 9594294894b..d5d02f9f931 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs @@ -89,13 +89,76 @@ void M() { """; await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); var locationTyped = testLspServer.GetLocations("type").Single(); + await AssertFormatDocumentOnTypeAsync(testLspServer, "\n", locationTyped, """ + class A + { + void M() { + + } + } + """); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/8429")] + public async Task TestFormatDocumentOnType_NewLineBeforeMultilineComment(bool mutatingLspWorkspace) + { + var markup = + """ + class A + { + void M() + { + } + {|type:|} + /* + private void Do() { } + */ + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var locationTyped = testLspServer.GetLocations("type").Single(); + await AssertFormatDocumentOnTypeAsync(testLspServer, "\n", locationTyped, """ + class A + { + void M() + { + } + + /* + private void Do() { } + */ + } + """); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/8429")] + public async Task TestFormatDocumentOnType_NewLineBeforeMultilineComment2(bool mutatingLspWorkspace) + { + var markup = + """ + class A + { + void M() + { + } + {|type:|} + /* + private void Do() { } + */ + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var locationTyped = testLspServer.GetLocations("type").Single(); await AssertFormatDocumentOnTypeAsync(testLspServer, "\n", locationTyped, """ class A { void M() { - } + + /* + private void Do() { } + */ } """); } @@ -122,7 +185,7 @@ private static LSP.DocumentOnTypeFormattingParams CreateDocumentOnTypeFormatting LSP.Location locationTyped, bool insertSpaces, int tabSize) - => new LSP.DocumentOnTypeFormattingParams() + => new() { Position = locationTyped.Range.Start, Character = characterTyped, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs index cd8b7470604..dcd108c0968 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs @@ -96,7 +96,7 @@ private static LSP.DocumentRangeFormattingParams CreateDocumentRangeFormattingPa LSP.Location location, bool insertSpaces, int tabSize) - => new LSP.DocumentRangeFormattingParams() + => new() { Range = location.Range, TextDocument = CreateTextDocumentIdentifier(location.DocumentUri), diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs index 834a9909f24..0f3678e26c8 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs @@ -316,7 +316,7 @@ private static async Task AssertFormatDocumentAsync( } private static LSP.DocumentFormattingParams CreateDocumentFormattingParams(DocumentUri uri, bool insertSpaces, int tabSize) - => new LSP.DocumentFormattingParams() + => new() { TextDocument = CreateTextDocumentIdentifier(uri), Options = new LSP.FormattingOptions() diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs index b935492779f..a2f7157944b 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs @@ -113,7 +113,7 @@ void M() } private static LSP.DocumentHighlight CreateDocumentHighlight(LSP.DocumentHighlightKind kind, LSP.Location location) - => new LSP.DocumentHighlight() + => new() { Kind = kind, Range = location.Range diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs index 8cd32466ff7..cf6ac808c2b 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/MapCode/MapCodeTests.cs @@ -48,7 +48,7 @@ public MapCodeTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) } private static ClientCapabilities CreateClientCapabilities(bool supportDocumentChanges) - => new LSP.ClientCapabilities + => new() { Workspace = new LSP.WorkspaceClientCapabilities { diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs index 57d63821359..7ef6764930a 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -23,7 +24,7 @@ public OnAutoInsertTests(ITestOutputHelper testOutputHelper) : base(testOutputHe [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|} @@ -35,8 +36,8 @@ void M() class A { /// - /// $0 - /// + /// $0 + /// void M() { } @@ -45,7 +46,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter_WithComment(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|} This is an existing comment @@ -57,8 +58,8 @@ void M() class A { /// - /// $0This is an existing comment - /// + /// $0This is an existing comment + /// void M() { } @@ -67,7 +68,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter_WithComment_NoSpace(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|}This is an existing comment @@ -79,8 +80,8 @@ void M() class A { /// - /// $0This is an existing comment - /// + /// $0This is an existing comment + /// void M() { } @@ -89,7 +90,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_CommentCharacter_VB(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("'", """ + => VerifyCSharpMarkupAndExpected("'", """ Class A '''{|type:|} Sub M() @@ -98,8 +99,8 @@ End Class """, """ Class A ''' - ''' $0 - ''' + ''' $0 + ''' Sub M() End Sub End Class @@ -107,7 +108,7 @@ End Class [Theory, CombinatorialData] public Task OnAutoInsert_ParametersAndReturns(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("/", """ + => VerifyCSharpMarkupAndExpected("/", """ class A { ///{|type:|} @@ -119,11 +120,11 @@ string M(int foo, bool bar) class A { /// - /// $0 - /// - /// - /// - /// + /// $0 + /// + /// + /// + /// string M(int foo, bool bar) { } @@ -156,7 +157,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_EnterKey(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { /// @@ -182,7 +183,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_EnterKey2(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { /// @@ -208,7 +209,7 @@ void M() [Theory, CombinatorialData] public Task OnAutoInsert_EnterKey3(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { /// @@ -234,7 +235,7 @@ string M(int foo, bool bar) [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormatting(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { void M() {{|type:|} @@ -248,11 +249,11 @@ void M() $0 } } - """, mutatingLspWorkspace, serverKind: WellKnownLspServerKinds.RazorLspServer); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormattingWithTabs(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { void M() {{|type:|} @@ -266,11 +267,11 @@ void M() $0 } } - """, mutatingLspWorkspace, insertSpaces: false, tabSize: 4, serverKind: WellKnownLspServerKinds.RazorLspServer); + """, mutatingLspWorkspace, insertSpaces: false, tabSize: 4, useVSCapabilities: false); [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormattingInsideMethod(bool mutatingLspWorkspace) - => VerifyMarkupAndExpected("\n", """ + => VerifyCSharpMarkupAndExpected("\n", """ class A { void M() @@ -290,7 +291,7 @@ void M() } } } - """, mutatingLspWorkspace, serverKind: WellKnownLspServerKinds.RazorLspServer); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData] public Task OnAutoInsert_BraceFormattingNoResultInInterpolation(bool mutatingLspWorkspace) @@ -302,7 +303,7 @@ void M() var s = $"Hello {{|type:|} } } - """, mutatingLspWorkspace); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1260219")] public Task OnAutoInsert_BraceFormattingDoesNotInsertExtraEmptyLines(bool mutatingLspWorkspace) @@ -315,7 +316,7 @@ void M() {|type:|} } } - """, mutatingLspWorkspace); + """, mutatingLspWorkspace, useVSCapabilities: false); [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1260219")] public Task OnAutoInsert_BraceFormattingDoesNotMoveCaretOnEnterInsideBraces(bool mutatingLspWorkspace) @@ -328,26 +329,75 @@ void M() } } - """, mutatingLspWorkspace); + """, mutatingLspWorkspace, useVSCapabilities: false); + + [Theory, CombinatorialData] + public Task OnAutoInsert_BraceFormattingOnNewLine(bool mutatingLspWorkspace) + => VerifyCSharpMarkupAndExpected("\n", """ + class A + { + void M() {{|type:|} + + } + } + """, """ + class A + { + void M() + { + $0 + } + } + """, mutatingLspWorkspace, useVSCapabilities: false); + + [Theory, CombinatorialData] + public Task OnAutoInsert_NoBraceFormattingForVS(bool mutatingLspWorkspace) + => VerifyNoResult("\n", """ + class A + { + void M() {{|type:|} + } + } + """, mutatingLspWorkspace, useVSCapabilities: true); - private async Task VerifyMarkupAndExpected( + [Theory, CombinatorialData] + public Task OnAutoInsert_BraceFormattingForRazorVS(bool mutatingLspWorkspace, bool useVSCapabilities) + => VerifyCSharpMarkupAndExpected("\n", """ + class A + { + void M() {{|type:|} + } + } + """, """ + class A + { + void M() + { + $0 + } + } + """, mutatingLspWorkspace, serverKind: WellKnownLspServerKinds.RazorLspServer, useVSCapabilities: useVSCapabilities); + + private async Task VerifyCSharpMarkupAndExpected( string characterTyped, - string markup, - string expected, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, bool mutatingLspWorkspace, bool insertSpaces = true, int tabSize = 4, string languageName = LanguageNames.CSharp, - WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) + WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer, + bool useVSCapabilities = true) { + var capbilities = GetCapabilities(useVSCapabilities); Task testLspServerTask; if (languageName == LanguageNames.CSharp) { - testLspServerTask = CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, ServerKind = serverKind }); + testLspServerTask = CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = capbilities, ServerKind = serverKind }); } else if (languageName == LanguageNames.VisualBasic) { - testLspServerTask = CreateVisualBasicTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = CapabilitiesWithVSExtensions, ServerKind = serverKind }); + testLspServerTask = CreateVisualBasicTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions { ClientCapabilities = capbilities, ServerKind = serverKind }); } else { @@ -368,9 +418,21 @@ private async Task VerifyMarkupAndExpected( Assert.Equal(expected, actualText); } - private async Task VerifyNoResult(string characterTyped, string markup, bool mutatingLspWorkspace, bool insertSpaces = true, int tabSize = 4) + private async Task VerifyNoResult( + string characterTyped, + string markup, + bool mutatingLspWorkspace, + bool insertSpaces = true, + int tabSize = 4, + WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer, + bool useVSCapabilities = true) { - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + var initilizationOptions = new InitializationOptions + { + ClientCapabilities = GetCapabilities(useVSCapabilities), + ServerKind = serverKind + }; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, initilizationOptions); var locationTyped = testLspServer.GetLocations("type").Single(); var documentText = await (await testLspServer.GetDocumentAsync(locationTyped.DocumentUri)).GetTextAsync(); @@ -395,7 +457,7 @@ private static LSP.VSInternalDocumentOnAutoInsertParams CreateDocumentOnAutoInse LSP.Location locationTyped, bool insertSpaces, int tabSize) - => new LSP.VSInternalDocumentOnAutoInsertParams + => new() { Position = locationTyped.Range.Start, Character = characterTyped, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs index c0177488c42..bc9c5b4e245 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs @@ -110,7 +110,7 @@ public async Task SwitchingContextsChangesDefaultContext(bool mutatingLspWorkspa } private static LSP.VSGetProjectContextsParams CreateGetProjectContextParams(DocumentUri uri) - => new LSP.VSGetProjectContextsParams() + => new() { TextDocument = new LSP.TextDocumentItem { DocumentUri = uri } }; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProtocolConversionsTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProtocolConversionsTests.cs index f9b1ff47184..b6ddc8d4439 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProtocolConversionsTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/ProtocolConversionsTests.cs @@ -245,13 +245,48 @@ void M() Assert.Equal(32, textSpan.End); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80119")] + public void RangeToTextSpanDoesNotThrow_WhenReferencingStartOfNextLineAfterLastLine() + { + var markup = GetTestMarkup(); + var sourceText = SourceText.From(markup); + + // The spec allows clients to send a range referencing the start of the next line + // after the last line in the document (and outside the bounds of the document). + // This should not throw. + var lastLineIndex = sourceText.Lines.Count - 1; + var range = new Range() + { + Start = new Position(lastLineIndex, 0), + End = new Position(lastLineIndex + 1, 0) + }; + + var textSpan = ProtocolConversions.RangeToTextSpan(range, sourceText); + + // Should span from the start of the last line to the end of the document + var lastLine = sourceText.Lines[lastLineIndex]; + Assert.Equal(lastLine.Start, textSpan.Start); + Assert.Equal(sourceText.Length, textSpan.End); + } + + [Fact] + public void RangeToTextSpanThrows_LineOutOfRange() + { + var markup = GetTestMarkup(); + var sourceText = SourceText.From(markup); + + // Ranges that are outside the document bounds should throw. + var range = new Range() { Start = new Position(0, 0), End = new Position(sourceText.Lines.Count + 1, 0) }; + Assert.Throws(() => ProtocolConversions.RangeToTextSpan(range, sourceText)); + } + [Fact] - public void RangeToTextSpanLineOutOfRangeError() + public void RangeToTextSpanWThrows_CharacterOutOfRange() { var markup = GetTestMarkup(); var sourceText = SourceText.From(markup); - var range = new Range() { Start = new Position(0, 0), End = new Position(sourceText.Lines.Count, 0) }; + var range = new Range() { Start = new Position(0, 0), End = new Position(sourceText.Lines.Count, 5) }; Assert.Throws(() => ProtocolConversions.RangeToTextSpan(range, sourceText)); } diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index b36ed1edb78..8f8624f2559 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -386,7 +386,7 @@ public void M() } private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret, IProgress progress) - => new LSP.ReferenceParams() + => new() { TextDocument = CreateTextDocumentIdentifier(caret.DocumentUri), Position = caret.Range.Start, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs index 6ef1e4b9acf..a449ae19291 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/PrepareRenameTests.cs @@ -93,7 +93,7 @@ void M() } private static LSP.PrepareRenameParams CreatePrepareRenameParams(LSP.Location location) - => new LSP.PrepareRenameParams() + => new() { Position = location.Range.Start, TextDocument = CreateTextDocumentIdentifier(location.DocumentUri) diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs index ba43f0733ff..0c928fe2ed9 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs @@ -372,7 +372,7 @@ public void M() } private static LSP.RenameParams CreateRenameParams(LSP.Location location, string newName) - => new LSP.RenameParams() + => new() { NewName = newName, Position = location.Range.Start, diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs index 2392c4ad043..44ca13aa3f6 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs @@ -51,20 +51,20 @@ private protected static IReadOnlyDictionary GetTokenTypeToIndex(Te } private static LSP.SemanticTokensFullParams CreateSemanticTokensFullParams(LSP.Location caret) - => new LSP.SemanticTokensFullParams + => new() { TextDocument = new LSP.TextDocumentIdentifier { DocumentUri = caret.DocumentUri } }; private static LSP.SemanticTokensRangeParams CreateSemanticTokensRangeParams(LSP.Location location) - => new LSP.SemanticTokensRangeParams + => new() { TextDocument = new LSP.TextDocumentIdentifier { DocumentUri = location.DocumentUri }, Range = location.Range }; private static SemanticTokensRangesParams CreateSemanticTokensRangesParams(LSP.Location caret, Range[] ranges) - => new SemanticTokensRangesParams + => new() { TextDocument = new LSP.TextDocumentIdentifier { DocumentUri = caret.DocumentUri }, Ranges = ranges diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs index fbdc1a5b01c..fd994d88a00 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs @@ -99,7 +99,6 @@ private void __RazorDirectiveTokenHelpers__() { #pragma warning disable 0414 private static object __o = null; #pragma warning restore 0414 - #pragma warning disable 1998 public async override global::System.Threading.Tasks.Task ExecuteAsync() { #nullable restore @@ -122,7 +121,7 @@ private void __RazorDirectiveTokenHelpers__() { var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); ImmutableArray spans = [ new LinePositionSpan(new LinePosition(12, 0), new LinePosition(13, 0)), - new LinePositionSpan(new LinePosition(29, 0), new LinePosition(30, 0)), + new LinePositionSpan(new LinePosition(28, 0), new LinePosition(29, 0)), ]; var options = ClassificationOptions.Default; @@ -141,7 +140,7 @@ private void __RazorDirectiveTokenHelpers__() { 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Number], 0, // '1' 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 17, 3, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 16, 3, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' ]; @@ -156,7 +155,7 @@ private void __RazorDirectiveTokenHelpers__() { 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Number], 0, // '1' 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 17, 3, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' + 16, 3, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' ]; diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs index 5073b90d423..2365e65e5e6 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs @@ -111,7 +111,7 @@ public async Task TestGetSignatureHelpServerCapabilitiesAsync(bool mutatingLspWo } private static LSP.SignatureInformation CreateSignatureInformation(string methodLabal, string methodDocumentation, string parameterLabel, string parameterDocumentation) - => new LSP.SignatureInformation() + => new() { Documentation = CreateMarkupContent(LSP.MarkupKind.PlainText, methodDocumentation), Label = methodLabal, @@ -122,7 +122,7 @@ private static LSP.SignatureInformation CreateSignatureInformation(string method }; private static LSP.ParameterInformation CreateParameterInformation(string parameter, string documentation) - => new LSP.ParameterInformation() + => new() { Documentation = CreateMarkupContent(LSP.MarkupKind.PlainText, documentation), Label = parameter diff --git a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs index 5dcebdca295..a73ed3f28de 100644 --- a/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs +++ b/src/roslyn/src/LanguageServer/ProtocolUnitTests/SimplifyMethod/SimplifyMethodTests.cs @@ -53,7 +53,7 @@ class A } private static SimplifyMethodParams CreateSimplifyMethodParams(LSP.Location location, string newText) - => new SimplifyMethodParams() + => new() { TextDocument = CreateTextDocumentIdentifier(location.DocumentUri), TextEdit = new TextEdit() { Range = location.Range, NewText = newText }, diff --git a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props index 710e106bed4..4df1e637691 100644 --- a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props +++ b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props @@ -6,6 +6,9 @@ <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ Custom + $(_RoslynTasksDirectory)bincore\ + $(RoslynCoreAssembliesPath) + $(_RoslynTasksDirectory) $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props index 192ae2959ec..f94f7243e67 100644 --- a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props +++ b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/Framework/build/Microsoft.Net.Compilers.Toolset.Framework.Core.props @@ -5,6 +5,7 @@ <_RoslynTargetDirectoryName>net472 <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ Custom + $(_RoslynTasksDirectory) $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props index 499e9baaebb..5b5eeeaec82 100644 --- a/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props +++ b/src/roslyn/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props @@ -4,6 +4,7 @@ <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\net472\ Custom + $(_RoslynTasksDirectory) $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj b/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj index 842a3f09379..b7a16d217ad 100644 --- a/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj +++ b/src/roslyn/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj @@ -41,6 +41,8 @@ + + diff --git a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj index f085a7df5c6..821bb31193c 100644 --- a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj +++ b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj @@ -27,6 +27,8 @@ + + diff --git a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj index ff7a519a245..87a6765126f 100644 --- a/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj +++ b/src/roslyn/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj @@ -25,6 +25,8 @@ + + diff --git a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs index f3511e2f954..a39c4bdaeae 100644 --- a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs +++ b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs @@ -32,8 +32,7 @@ internal sealed class DisposeAnalysisHelper "System.IO.TextWriter", "System.Resources.IResourceReader", ]; - private static readonly BoundedCacheWithFactory s_DisposeHelperCache = - new(); + private static readonly BoundedCacheWithFactory s_DisposeHelperCache = new(); private static readonly ImmutableHashSet s_DisposableCreationKinds = ImmutableHashSet.Create( OperationKind.ObjectCreation, diff --git a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs index 7dd00108a17..ea6dd28651c 100644 --- a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs +++ b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs @@ -24,8 +24,7 @@ public abstract class DataFlowAnalysis where TBlockAnalysisResult : AbstractBlockAnalysisResult { - private static readonly BoundedCache> s_resultCache = - new(); + private static readonly BoundedCache> s_resultCache = new(); protected DataFlowAnalysis(AbstractAnalysisDomain analysisDomain, DataFlowOperationVisitor operationVisitor) { diff --git a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs index 1f675630f5f..fe689c29120 100644 --- a/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs +++ b/src/roslyn/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs @@ -20,8 +20,7 @@ namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow /// internal static class LValueFlowCapturesProvider { - private static readonly ConditionalWeakTable> s_lValueFlowCapturesCache = - new(); + private static readonly ConditionalWeakTable> s_lValueFlowCapturesCache = new(); public static ImmutableHashSet GetOrCreateLValueFlowCaptures(ControlFlowGraph cfg) => s_lValueFlowCapturesCache.GetValue(cfg, CreateLValueFlowCaptures); diff --git a/src/roslyn/src/Scripting/CSharpTest/CommandLineRunnerTests.cs b/src/roslyn/src/Scripting/CSharpTest/CommandLineRunnerTests.cs index 874cd142ca8..2d3c526ec24 100644 --- a/src/roslyn/src/Scripting/CSharpTest/CommandLineRunnerTests.cs +++ b/src/roslyn/src/Scripting/CSharpTest/CommandLineRunnerTests.cs @@ -52,9 +52,6 @@ select x * x . {{ . return new int[] {{ 1, 2, 3, 4, 5 }}; . }} -«Yellow» -(1,19): warning CS1998: {CSharpResources.WRN_AsyncLacksAwaits} -«Gray» > from x in await GetStuffAsync() . where x > 2 . select x * x @@ -62,7 +59,7 @@ . select x * x > ", runner.Console.Out.ToString()); AssertEx.AssertEqualToleratingWhitespaceDifferences( - $@"(1,19): warning CS1998: {CSharpResources.WRN_AsyncLacksAwaits}", + "", runner.Console.Error.ToString()); } diff --git a/src/roslyn/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs b/src/roslyn/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs index a90bb163156..3b324897b76 100644 --- a/src/roslyn/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs +++ b/src/roslyn/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs @@ -186,7 +186,6 @@ internal static class CodeActionDescriptions { "Microsoft.CodeAnalysis.CSharp.PopulateSwitch.CSharpPopulateSwitchExpressionCodeFixProvider", "Populate Switch: Expression" }, { "Microsoft.CodeAnalysis.CSharp.PopulateSwitch.CSharpPopulateSwitchStatementCodeFixProvider", "Populate Switch: Statement" }, { "Microsoft.CodeAnalysis.CSharp.QualifyMemberAccess.CSharpQualifyMemberAccessCodeFixProvider", "Qualify Member Access" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier.CSharpRemoveAsyncModifierCodeFixProvider", "Remove Async Modifier" }, { "Microsoft.CodeAnalysis.CSharp.RemoveConfusingSuppression.CSharpRemoveConfusingSuppressionCodeFixProvider", "Remove Confusing Suppression" }, { "Microsoft.CodeAnalysis.CSharp.RemoveInKeyword.RemoveInKeywordCodeFixProvider", "Remove In Keyword" }, { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryCast.CSharpRemoveUnnecessaryCastCodeFixProvider", "Remove Unnecessary Cast" }, diff --git a/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs b/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs index 6cf1af2ac8e..718747a8e8b 100644 --- a/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs +++ b/src/roslyn/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs @@ -180,7 +180,10 @@ private bool CheckPackages(TextWriter textWriter) excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.DiaSymReader.Native", PathComparison) || relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.dll", PathComparison) || - (relativeFileName.StartsWith(@"tasks\netcore\binfx\", PathComparison) && relativeFileName.EndsWith(".targets", PathComparison)), + (relativeFileName.StartsWith(@"tasks\netcore\binfx\", PathComparison) && relativeFileName.EndsWith(".targets", PathComparison)) || + relativeFileName.Equals(@"tasks\netcore\bincore\csc.exe", PathComparison) || + relativeFileName.Equals(@"tasks\netcore\bincore\vbc.exe", PathComparison) || + relativeFileName.Equals(@"tasks\netcore\bincore\VBCSCompiler.exe", PathComparison), (@"tasks\net472", GetProjectOutputDirectory("csc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("vbc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("csi", "net472")), diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs deleted file mode 100644 index 7e1720526fa..00000000000 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/AbstractRazorCohostLifecycleService.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; - -internal abstract class AbstractRazorCohostLifecycleService : IDisposable -{ - public abstract Task LspServerIntializedAsync(CancellationToken cancellationToken); - public abstract Task RazorActivatedAsync(ClientCapabilities clientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken); - public abstract void Dispose(); -} diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs index b909cb0ea84..dd849edb980 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Constants.cs @@ -13,6 +13,4 @@ internal static class Constants // These UI contexts are provided by Razor, so must match https://github.com/dotnet/razor/blob/main/src/Razor/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorConstants.cs public static readonly Guid RazorCohostingUIContext = new Guid("6d5b86dc-6b8a-483b-ae30-098a3c7d6774"); - - public static readonly Guid RazorCapabilityPresentUIContext = new Guid("2077a158-ee71-484c-be76-350a1d49eaea"); } diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs index e688a3759f3..645103db784 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/Handlers/OnAutoInsert.cs @@ -26,6 +26,6 @@ internal static class OnAutoInsert predicate: s => s.Metadata.Language == LanguageNames.CSharp, selector: s => s.Value); - return OnAutoInsertHandler.GetOnAutoInsertResponseAsync(globalOptions, services, document, linePosition, character, formattingOptions, isRazorRequest: true, cancellationToken); + return OnAutoInsertHandler.GetOnAutoInsertResponseAsync(globalOptions, services, document, linePosition, character, formattingOptions, includeNewLineBraceFormatting: true, cancellationToken); } } diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs index 87b350649cd..b35f55862b0 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/ICohostStartupService.cs @@ -8,7 +8,6 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; -[Obsolete("Please move to AbstractRazorCohostLifecycleService. This will be removed in a future release.")] internal interface ICohostStartupService { Task StartupAsync(string serializedClientCapabilities, RazorCohostRequestContext requestContext, CancellationToken cancellationToken); diff --git a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs index 4d58661ee7a..05d547e6505 100644 --- a/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs +++ b/src/roslyn/src/Tools/ExternalAccess/Razor/Features/Cohost/RazorStartupServiceFactory.cs @@ -22,34 +22,22 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class RazorStartupServiceFactory( [Import(AllowDefault = true)] IUIContextActivationService? uIContextActivationService, - [Import(AllowDefault = true)] Lazy? cohostStartupService, - [Import(AllowDefault = true)] Lazy? razorCohostLifecycleService) : ILspServiceFactory + [Import(AllowDefault = true)] Lazy? cohostStartupService) : ILspServiceFactory { public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) { - return new RazorStartupService(uIContextActivationService, cohostStartupService, razorCohostLifecycleService); + return new RazorStartupService(uIContextActivationService, cohostStartupService); } private class RazorStartupService( IUIContextActivationService? uIContextActivationService, -#pragma warning disable CS0618 // Type or member is obsolete - Lazy? cohostStartupService, -#pragma warning restore CS0618 // Type or member is obsolete - Lazy? razorCohostLifecycleService) : ILspService, IOnInitialized, IDisposable + Lazy? cohostStartupService) : ILspService, IOnInitialized, IDisposable { private readonly CancellationTokenSource _disposalTokenSource = new(); private IDisposable? _cohostActivation; - private IDisposable? _razorFilePresentActivation; public void Dispose() { - if (razorCohostLifecycleService is { IsValueCreated: true, Value: var service }) - { - service.Dispose(); - } - - _razorFilePresentActivation?.Dispose(); - _razorFilePresentActivation = null; _cohostActivation?.Dispose(); _cohostActivation = null; _disposalTokenSource.Cancel(); @@ -63,53 +51,32 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon return Task.CompletedTask; } - if (cohostStartupService is null && razorCohostLifecycleService is null) + if (cohostStartupService is null) { return Task.CompletedTask; } if (uIContextActivationService is null) { - PreinitializeRazor(); InitializeRazor(); } else { - // There are two initialization methods for Razor, which looks odd here, but are really controlled by UI contexts. - // This method fires for any Roslyn project, but not all Roslyn projects are Razor projects, so the first UI context - // triggers where there is a project with a Razor capability present in the solution, and the next is when a Razor file - // is opened in the editor. ie these two lines look the same, but really they do different levels of initialization. - _razorFilePresentActivation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCapabilityPresentUIContext, PreinitializeRazor); _cohostActivation = uIContextActivationService.ExecuteWhenActivated(Constants.RazorCohostingUIContext, InitializeRazor); } return Task.CompletedTask; - void PreinitializeRazor() - { - this.PreinitializeRazorAsync(_disposalTokenSource.Token).ReportNonFatalErrorAsync(); - } - void InitializeRazor() { this.InitializeRazorAsync(clientCapabilities, context, _disposalTokenSource.Token).ReportNonFatalErrorAsync(); } } - private async Task PreinitializeRazorAsync(CancellationToken cancellationToken) - { - if (cancellationToken.IsCancellationRequested) return; - - await TaskScheduler.Default.SwitchTo(alwaysYield: true); - - if (razorCohostLifecycleService is not null) - { - await razorCohostLifecycleService.Value.LspServerIntializedAsync(cancellationToken).ConfigureAwait(false); - } - } - private async Task InitializeRazorAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) { + Contract.ThrowIfNull(cohostStartupService); + // The LSP server will dispose us when the server exits, but VS could decide to activate us later. // If a new instance of the server is created, a new instance of this class will be created and the // UIContext will already be active, so this method will be immediately called on the new instance. @@ -121,18 +88,9 @@ private async Task InitializeRazorAsync(ClientCapabilities clientCapabilities, R var requestContext = new RazorCohostRequestContext(context); - if (razorCohostLifecycleService is not null) - { - // If we have a cohost lifecycle service, fire post-initialization, which happens when the UIContext is activated. - await razorCohostLifecycleService.Value.RazorActivatedAsync(clientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); - } - - if (cohostStartupService is not null) - { - // We use a string to pass capabilities to/from Razor to avoid version issues with the Protocol DLL - var serializedClientCapabilities = JsonSerializer.Serialize(clientCapabilities, ProtocolConversions.LspJsonSerializerOptions); - await cohostStartupService.Value.StartupAsync(serializedClientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); - } + // We use a string to pass capabilities to/from Razor to avoid version issues with the Protocol DLL + var serializedClientCapabilities = JsonSerializer.Serialize(clientCapabilities, ProtocolConversions.LspJsonSerializerOptions); + await cohostStartupService.Value.StartupAsync(serializedClientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs b/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs index 1731fc22059..0d767957de9 100644 --- a/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs +++ b/src/roslyn/src/Tools/PrepareTests/MinimizeUtil.cs @@ -16,6 +16,11 @@ internal static class MinimizeUtil { internal record FilePathInfo(string RelativeDirectory, string Directory, string RelativePath, string FullPath); + /// + /// Special group for collecting files which need chmod +x. + /// + private static readonly Guid s_executablesGroup = new Guid("2665eb42-0a7d-4ea2-bb92-e4251d48df44"); + internal static void Run(string sourceDirectory, string destinationDirectory, bool isUnix) { const string duplicateDirectoryName = ".duplicate"; @@ -35,13 +40,13 @@ internal static void Run(string sourceDirectory, string destinationDirectory, bo CreateHardLink(outputPath, Path.Combine(sourceDirectory, individualFile)); } - // Map of all PE files MVID to the path information + // Map of all PE files MVID (or s_executablesGroup) to the path information var idToFilePathMap = initialWalk(); resolveDuplicates(); writeHydrateFile(); // The goal of initial walk is to - // 1. Record any PE files as they are eligable for de-dup + // 1. Record any PE files as they are eligible for de-dup // 2. Hard link all other files into destination directory Dictionary> initialWalk() { @@ -66,7 +71,7 @@ .. Directory.EnumerateDirectories(artifactsDir, "RunTests") return idToFilePathMap; } - static IEnumerable<(Guid mvid, FilePathInfo pathInfo)> walkDirectory(string unitDirPath, string sourceDirectory, string destinationDirectory) + IEnumerable<(Guid mvid, FilePathInfo pathInfo)> walkDirectory(string unitDirPath, string sourceDirectory, string destinationDirectory) { Console.WriteLine($"Walking {unitDirPath}"); string? lastOutputDirectory = null; @@ -95,6 +100,15 @@ .. Directory.EnumerateDirectories(artifactsDir, "RunTests") { var destFilePath = Path.Combine(currentOutputDirectory, fileName); CreateHardLink(destFilePath, sourceFilePath); + + if (isUnix && !OperatingSystem.IsWindows() && File.GetUnixFileMode(sourceFilePath).HasFlag(UnixFileMode.UserExecute)) + { + yield return (s_executablesGroup, new FilePathInfo( + RelativeDirectory: currentRelativeDirectory, + Directory: currentDirName, + RelativePath: Path.Combine(currentRelativeDirectory, fileName), + FullPath: sourceFilePath)); + } } } } @@ -104,6 +118,11 @@ void resolveDuplicates() { foreach (var pair in idToFilePathMap) { + if (pair.Key == s_executablesGroup) + { + continue; + } + if (pair.Value.Count > 1) { CreateHardLink(getPeFilePath(pair.Key), pair.Value[0].FullPath); @@ -185,6 +204,11 @@ static void writeWindowsRehydrateContent(StringBuilder builder, IGrouping RunAsync(ReplayOptions options) Console.WriteLine($"Pipe Name: {options.PipeName}"); Console.WriteLine($"Parallel: {options.MaxParallel}"); Console.WriteLine($"Iterations: {options.Iterations}"); + + var sumBuildTime = TimeSpan.Zero; + var sumTotalTime = TimeSpan.Zero; + try + { + var compilerCalls = ReadAllCompilerCalls(options.BinlogPath); + Console.WriteLine($"Compilation Events: {compilerCalls.Count}"); + + for (var i = 0; i < options.Iterations; i++) + { + Console.WriteLine(); + Console.WriteLine($"Iteration: {i + 1}"); + var (buildTime, totalTime) = await RunOneAsync(compilerCalls, options).ConfigureAwait(false); + Console.WriteLine($"Build Time: {buildTime:mm\\:ss}"); + Console.WriteLine($"Total Time: {totalTime:mm\\:ss}"); + + sumBuildTime += buildTime; + sumTotalTime += totalTime; + } + + if (options.Iterations > 1) + { + Console.WriteLine(); + Console.WriteLine($"Compilation Events: {compilerCalls.Count}"); + Console.WriteLine($"Average Build Time: {TimeSpan.FromTicks(sumBuildTime.Ticks / options.Iterations):mm\\:ss}"); + Console.WriteLine($"Average Total Time: {TimeSpan.FromTicks(sumTotalTime.Ticks / options.Iterations):mm\\:ss}"); + } + + foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables()) + { + if (entry.Key is string key && key.StartsWith("DOTNET_", StringComparison.OrdinalIgnoreCase)) + { + Console.WriteLine($"{key}={entry.Value}"); + } + } + + return 0; + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + return 1; + } +} + +static async Task<(TimeSpan BuildTime, TimeSpan TotalTime)> RunOneAsync(List compilerCalls, ReplayOptions options) +{ Console.WriteLine(); Console.WriteLine("Starting server"); + if (Directory.Exists(options.OutputDirectory)) + { + Directory.Delete(options.OutputDirectory, recursive: true); + } + Directory.CreateDirectory(options.OutputDirectory); + Directory.CreateDirectory(options.TempDirectory); + using var compilerServerLogger = new CompilerServerLogger("replay", Path.Combine(options.OutputDirectory, "server.log")); if (!BuildServerConnection.TryCreateServer(options.ClientDirectory, options.PipeName, compilerServerLogger, out int serverProcessId)) { - Console.WriteLine("Failed to start server"); - return 1; + throw new Exception("Failed to create server"); } - Console.WriteLine($"Process Id: {serverProcessId}"); + Console.WriteLine($"VBCSCompiler Process Id: {serverProcessId}"); if (options.Wait) { Console.WriteLine("Press any key to continue"); @@ -95,28 +143,17 @@ static async Task RunAsync(ReplayOptions options) Console.WriteLine("Continuing"); } + var stopwatch = new Stopwatch(); + stopwatch.Start(); + TimeSpan buildTime; try { - for (var i = 0; i < options.Iterations; i++) + await foreach (var buildData in BuildAllAsync(options, compilerCalls, compilerServerLogger, CancellationToken.None).ConfigureAwait(false)) { - Console.WriteLine(); - Console.WriteLine($"Iteration: {i + 1}"); - var compilerCalls = ReadAllCompilerCalls(options.BinlogPath); - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - await foreach (var buildData in BuildAllAsync(options, compilerCalls, compilerServerLogger, CancellationToken.None).ConfigureAwait(false)) - { - Console.WriteLine($"{buildData.CompilerCall.GetDiagnosticName()} ... {buildData.BuildResponse.Type}"); - } - - stopwatch.Stop(); - Console.WriteLine($"Pipe Name: {options.PipeName}"); - Console.WriteLine($"Compilation Events: {compilerCalls.Count}"); - Console.WriteLine($"Time: {stopwatch.Elapsed:mm\\:ss}"); + Console.WriteLine($"{buildData.CompilerCall.GetDiagnosticName()} ... {buildData.BuildResponse.Type}"); } - return 0; + buildTime = stopwatch.Elapsed; } finally { @@ -129,6 +166,8 @@ await BuildServerConnection.RunServerShutdownRequestAsync( compilerServerLogger, cancellationToken: CancellationToken.None).ConfigureAwait(false); } + + return (buildTime, stopwatch.Elapsed); } static List ReadAllCompilerCalls(string binlogPath) diff --git a/src/roslyn/src/Tools/Replay/Replay.csproj b/src/roslyn/src/Tools/Replay/Replay.csproj index 180c31962b9..219d34dfc76 100644 --- a/src/roslyn/src/Tools/Replay/Replay.csproj +++ b/src/roslyn/src/Tools/Replay/Replay.csproj @@ -1,7 +1,7 @@  Exe - $(NetRoslyn);net472 + $(NetRoslynSourceBuild);net472 true false diff --git a/src/roslyn/src/Tools/SemanticSearch/Extensions/ProjectModel.cs b/src/roslyn/src/Tools/SemanticSearch/Extensions/ProjectModel.cs index 61a6c70afdb..3e068672407 100644 --- a/src/roslyn/src/Tools/SemanticSearch/Extensions/ProjectModel.cs +++ b/src/roslyn/src/Tools/SemanticSearch/Extensions/ProjectModel.cs @@ -107,7 +107,7 @@ internal static ResxFile ReadFromFile(string filePath) public ResxFile AddString(string name, string value) => new(FilePath, _changes.SetItem(name, value)); - internal string GetContent() + public string GetContent() { if (_changes.Count == 0) { diff --git a/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.SemanticSearch.Extensions.txt b/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.SemanticSearch.Extensions.txt index 27577624ea0..f1bf9035ec8 100644 --- a/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.SemanticSearch.Extensions.txt +++ b/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.SemanticSearch.Extensions.txt @@ -1,4 +1,4 @@ +T:* -M:Microsoft.CodeAnalysis.SemanticSearch.Extensions.ProjectModel.#ctor* -M:Microsoft.CodeAnalysis.SemanticSearch.Extensions.ProjectModel.GetChanges* - +-M:Microsoft.CodeAnalysis.SemanticSearch.Extensions.ResxFile.GetContent* \ No newline at end of file diff --git a/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index 6b1f60e975c..7e9430fccad 100644 --- a/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/roslyn/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -686,6 +686,7 @@ Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean) Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean,System.Boolean) Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean,System.Boolean,System.Func{System.Exception,System.Boolean}) +Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.#ctor(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions,System.Action{System.Exception,Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostic},System.Boolean,System.Boolean,System.Boolean,System.Func{System.Exception,System.Boolean},System.Func{Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer,Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptionsProvider}) Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.get_AnalyzerExceptionFilter Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.get_ConcurrentAnalysis Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions.get_LogAnalyzerExecutionTime @@ -1230,6 +1231,7 @@ Microsoft.CodeAnalysis.IMethodSymbol.GetTypeInferredDuringReduction(Microsoft.Co Microsoft.CodeAnalysis.IMethodSymbol.ReduceExtensionMethod(Microsoft.CodeAnalysis.ITypeSymbol) Microsoft.CodeAnalysis.IMethodSymbol.get_Arity Microsoft.CodeAnalysis.IMethodSymbol.get_AssociatedAnonymousDelegate +Microsoft.CodeAnalysis.IMethodSymbol.get_AssociatedExtensionImplementation Microsoft.CodeAnalysis.IMethodSymbol.get_AssociatedSymbol Microsoft.CodeAnalysis.IMethodSymbol.get_CallingConvention Microsoft.CodeAnalysis.IMethodSymbol.get_ConstructedFrom @@ -1284,8 +1286,12 @@ Microsoft.CodeAnalysis.INamedTypeSymbol.get_ConstructedFrom Microsoft.CodeAnalysis.INamedTypeSymbol.get_Constructors Microsoft.CodeAnalysis.INamedTypeSymbol.get_DelegateInvokeMethod Microsoft.CodeAnalysis.INamedTypeSymbol.get_EnumUnderlyingType +Microsoft.CodeAnalysis.INamedTypeSymbol.get_ExtensionGroupingName +Microsoft.CodeAnalysis.INamedTypeSymbol.get_ExtensionMarkerName +Microsoft.CodeAnalysis.INamedTypeSymbol.get_ExtensionParameter Microsoft.CodeAnalysis.INamedTypeSymbol.get_InstanceConstructors Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsComImport +Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsExtension Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsFileLocal Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsGenericType Microsoft.CodeAnalysis.INamedTypeSymbol.get_IsImplicitClass diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs index 676b5480511..19bedf59061 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs @@ -11,7 +11,7 @@ namespace BoundTreeGenerator { - internal enum TargetLanguage + public enum TargetLanguage { VB, CSharp diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/Program.cs b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/Program.cs index 8283f8ed4e0..435b8fa2806 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/Program.cs +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/Program.cs @@ -12,25 +12,18 @@ namespace BoundTreeGenerator { - internal class Program + public static class Program { private static int Main(string[] args) { - string language; - string infilename; - string outfilename; TargetLanguage targetLanguage; - if (args.Length != 3) + if (args is not [string language, string infilename, string outfilename]) { Console.Error.WriteLine("Usage: \"{0} \", where is \"VB\" or \"CSharp\"", Path.GetFileNameWithoutExtension(args[0])); return 1; } - language = args[0]; - infilename = args[1]; - outfilename = args[2]; - switch (language) { case "VB": @@ -45,6 +38,11 @@ private static int Main(string[] args) return 1; } + return Generate(targetLanguage, infilename, outfilename); + } + + public static int Generate(TargetLanguage targetLanguage, string infilename, string outfilename) + { Tree tree; var serializer = new XmlSerializer(typeof(Tree)); using (var reader = XmlReader.Create(infilename, new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit })) diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/Program.cs b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/Program.cs index 00d84d57e89..4a433be5020 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/Program.cs +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/Program.cs @@ -16,7 +16,7 @@ public static class Program { public static int Main(string[] args) { - if (args.Length != 2) + if (args is not [string inputPath, string outputPath]) { Console.WriteLine( @"Usage: CSharpErrorFactsGenerator.exe input output @@ -26,9 +26,11 @@ input The path to ErrorCode.cs return -1; } - string inputPath = args[0]; - string outputPath = args[1]; + return Generate(inputPath, outputPath); + } + public static int Generate(string inputPath, string outputPath) + { var outputText = new StringBuilder(); outputText.AppendLine("namespace Microsoft.CodeAnalysis.CSharp"); outputText.AppendLine("{"); diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Program.cs b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Program.cs index 1b580c4d3a6..1fec07faac6 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Program.cs +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Program.cs @@ -17,7 +17,7 @@ namespace CSharpSyntaxGenerator { - internal static class Program + public static class Program { public static int Main(string[] args) { @@ -27,11 +27,6 @@ public static int Main(string[] args) } string inputFile = args[0]; - if (!File.Exists(inputFile)) - { - Console.WriteLine(inputFile + " not found."); - return 1; - } bool writeSource = true; bool writeTests = false; @@ -69,6 +64,17 @@ public static int Main(string[] args) } } + return Generate(inputFile, outputFile, writeSource, writeTests, writeGrammar, writeSignatures); + } + + public static int Generate(string inputFile, string outputFile, bool writeSource, bool writeTests, bool writeGrammar, bool writeSignatures) + { + if (!File.Exists(inputFile)) + { + Console.WriteLine(inputFile + " not found."); + return 1; + } + return writeGrammar ? WriteGrammarFile(inputFile, outputFile) : WriteCSharpSourceFiles(inputFile, writeSource, writeTests, writeSignatures, outputFile); diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/Program.cs b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/Program.cs index 7fe491e8b18..fdad52efe4d 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/Program.cs +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/IOperationGenerator/Program.cs @@ -6,31 +6,37 @@ using System.IO; using System.Xml; using System.Xml.Serialization; -using IOperationGenerator; -string inFileName; -string outFilePath; +namespace IOperationGenerator; -if (args.Length != 2) +public static class Program { - Console.Error.WriteLine("Usage: \"{0} \"", Path.GetFileNameWithoutExtension(args[0])); - return 1; -} + public static int Main(string[] args) + { + if (args is not [string inFilePath, string outFilePath]) + { + Console.Error.WriteLine("Usage: \"{0} \"", Path.GetFileNameWithoutExtension(args[0])); + return 1; + } -inFileName = args[0]; -outFilePath = args[1]; + return Generate(inFilePath, outFilePath); + } -Tree? tree; -var serializer = new XmlSerializer(typeof(Tree)); -using (var reader = XmlReader.Create(inFileName, new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit })) -{ - tree = (Tree?)serializer.Deserialize(reader); -} + public static int Generate(string inFilePath, string outFilePath) + { + Tree? tree; + var serializer = new XmlSerializer(typeof(Tree)); + using (var reader = XmlReader.Create(inFilePath, new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit })) + { + tree = (Tree?)serializer.Deserialize(reader); + } -if (tree is null) -{ - Console.WriteLine("Deserialize returned null."); - return 1; -} + if (tree is null) + { + Console.WriteLine("Deserialize returned null."); + return 1; + } -return IOperationClassWriter.Write(tree, outFilePath) ? 0 : 1; + return IOperationClassWriter.Write(tree, outFilePath) ? 0 : 1; + } +} diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/Program.vb b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/Program.vb index 5c643315803..7f7c9c18d55 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/Program.vb +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/Program.vb @@ -5,7 +5,7 @@ Imports System.IO Imports System.Text -Friend Module Program +Public Module Program Public Function Main(args As String()) As Integer If args.Length <> 2 Then Console.WriteLine( @@ -18,6 +18,10 @@ Friend Module Program Dim inputPath = args(0) Dim outputPath = args(1) + Return Generate(inputPath, outputPath) + End Function + + Public Function Generate(inputPath As String, outputPath As String) As Integer Dim outputText = New StringBuilder outputText.AppendLine("Namespace Microsoft.CodeAnalysis.VisualBasic") outputText.AppendLine(" Friend Partial Module ErrorFacts") diff --git a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/Program.vb b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/Program.vb index fbcf350bcbf..933f166bb05 100644 --- a/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/Program.vb +++ b/src/roslyn/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/Program.vb @@ -11,29 +11,38 @@ Imports System.Text ''' ''' Contains the startup code, command line argument processing, and driving the execution of the tool. ''' -Friend Module Program +Public Module Program Const exitWithErrors = 1, - exitWithoutErrors = 0 + exitWithoutErrors = 0 Public Function Main(args As String()) As Integer Try - Dim outputKind As String = Nothing + Dim writeSource = True + Dim writeTests = False Dim paths As New List(Of String)() Dim grammar = False For Each arg In args Dim c = arg.ToLowerInvariant() - If c = "/test" OrElse c = "/source" OrElse c = "/gettext" Then - If outputKind IsNot Nothing Then - PrintUsage() - Return exitWithErrors - End If - outputKind = c + If c = "/source" Then + writeSource = True + writeTests = False + grammar = False + ElseIf c = "/test" Then + writeSource = False + writeTests = True + grammar = False + ElseIf c = "/gettext" Then + writeSource = False + writeTests = False + grammar = False + ElseIf c = "/grammar" Then + writeSource = False + writeTests = False + grammar = True ElseIf c = "/?" Then PrintUsage() Return exitWithErrors - ElseIf c = "/grammar" Then - grammar = True Else paths.Add(arg) End If @@ -47,15 +56,8 @@ Friend Module Program Dim inputFile = paths(0) Dim outputFile = paths(1) - If Not File.Exists(inputFile) Then - Console.Error.WriteLine("Input file not found - ""{0}""", inputFile) - - Return exitWithErrors - End If + Return Generate(inputFile, outputFile, writeSource, writeTests, grammar) - Return If(grammar, - GenerateGrammar(inputFile, outputFile), - GenerateSource(outputKind, inputFile, outputFile)) Catch ex As Exception Console.Error.WriteLine("FATAL ERROR: {0}", ex.Message) Console.Error.WriteLine(ex.StackTrace) @@ -64,6 +66,18 @@ Friend Module Program End Try End Function + Public Function Generate(inputFile As String, outputFile As String, writeSource As Boolean, writeTests As Boolean, writeGrammar As Boolean) As Integer + If Not File.Exists(inputFile) Then + Console.Error.WriteLine("Input file not found - ""{0}""", inputFile) + + Return exitWithErrors + End If + + Return If(writeGrammar, + GenerateGrammar(inputFile, outputFile), + GenerateSource(writeSource, writeTests, inputFile, outputFile)) + End Function + Private Function GenerateGrammar(inputFile As String, outputFile As String) As Integer Dim definition As ParseTree = Nothing If Not TryReadDefinition(inputFile, definition) Then @@ -82,14 +96,14 @@ Friend Module Program Return exitWithoutErrors End Function - Private Function GenerateSource(outputKind As String, inputFile As String, outputFile As String) As Integer + Private Function GenerateSource(writeSource As Boolean, writeTests As Boolean, inputFile As String, outputFile As String) As Integer Dim definition As ParseTree = Nothing If Not TryReadDefinition(inputFile, definition) Then Return exitWithErrors End If Dim checksum = GetChecksum(inputFile) - WriteOutput(inputFile, outputFile, definition, outputKind, checksum) + WriteOutput(inputFile, outputFile, definition, writeSource, writeTests, checksum) Return exitWithoutErrors End Function @@ -120,34 +134,31 @@ Friend Module Program Return True End Function - Public Sub WriteOutput(inputFile As String, outputFile As String, definition As ParseTree, outputKind As String, checksum As String) - Select Case outputKind - Case "/test" - Using output As New StreamWriter(New FileStream(outputFile, FileMode.Create, FileAccess.Write), Encoding.UTF8) - - WriteHeader(output, checksum) - output.WriteLine() - output.WriteLine("Imports System.Collections.Generic") - output.WriteLine("Imports System.Collections.Immutable") - output.WriteLine("Imports System.Runtime.CompilerServices") - output.WriteLine("Imports Microsoft.CodeAnalysis.VisualBasic.Syntax") - output.WriteLine("Imports Roslyn.Utilities") - output.WriteLine("Imports Xunit") - - Dim testWriter As New TestWriter(definition, checksum) - testWriter.WriteTestCode(output) - End Using - - Case "/gettext" - Using output As New StreamWriter(New FileStream(outputFile, FileMode.Create, FileAccess.Write), Encoding.UTF8) - WriteHeader(output, checksum) - Dim syntaxFactsWriter As New SyntaxFactsWriter(definition) - syntaxFactsWriter.GenerateGetText(output) - End Using - - Case Else - WriteSyntax(inputFile, outputFile, definition, checksum) - End Select + Public Sub WriteOutput(inputFile As String, outputFile As String, definition As ParseTree, writeSource As Boolean, writeTests As Boolean, checksum As String) + If writeTests Then + Using output As New StreamWriter(New FileStream(outputFile, FileMode.Create, FileAccess.Write), Encoding.UTF8) + + WriteHeader(output, checksum) + output.WriteLine() + output.WriteLine("Imports System.Collections.Generic") + output.WriteLine("Imports System.Collections.Immutable") + output.WriteLine("Imports System.Runtime.CompilerServices") + output.WriteLine("Imports Microsoft.CodeAnalysis.VisualBasic.Syntax") + output.WriteLine("Imports Roslyn.Utilities") + output.WriteLine("Imports Xunit") + + Dim testWriter As New TestWriter(definition, checksum) + testWriter.WriteTestCode(output) + End Using + ElseIf writeSource Then + WriteSyntax(inputFile, outputFile, definition, checksum) + Else + Using output As New StreamWriter(New FileStream(outputFile, FileMode.Create, FileAccess.Write), Encoding.UTF8) + WriteHeader(output, checksum) + Dim syntaxFactsWriter As New SyntaxFactsWriter(definition) + syntaxFactsWriter.GenerateGetText(output) + End Using + End If End Sub Public Sub WriteSyntax(inputFile As String, outputFile As String, definition As ParseTree, checksum As String) diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/CSharpPackage.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/CSharpPackage.cs index 51b9e1a4233..d085fa72391 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/CSharpPackage.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/CSharpPackage.cs @@ -39,7 +39,6 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService; // Spacing // Wrapping // Naming -// IntelliSense [ProvideLanguageEditorOptionPage(typeof(Options.AdvancedOptionPage), "CSharp", null, "Advanced", pageNameResourceId: "#102", keywordListResourceId: 306)] [ProvideLanguageEditorToolsOptionCategory("CSharp", "Code Style", "#114")] @@ -51,9 +50,10 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService; [ProvideLanguageEditorOptionPage(typeof(Options.Formatting.FormattingNewLinesPage), "CSharp", @"Code Style\Formatting", "NewLines", pageNameResourceId: "#111", keywordListResourceId: 309)] [ProvideLanguageEditorOptionPage(typeof(Options.Formatting.FormattingSpacingPage), "CSharp", @"Code Style\Formatting", "Spacing", pageNameResourceId: "#112", keywordListResourceId: 310)] [ProvideLanguageEditorOptionPage(typeof(Options.NamingStylesOptionPage), "CSharp", @"Code Style", "Naming", pageNameResourceId: "#115", keywordListResourceId: 314)] -[ProvideLanguageEditorOptionPage(typeof(Options.IntelliSenseOptionPage), "CSharp", null, "IntelliSense", pageNameResourceId: "#103", keywordListResourceId: 312)] [ProvideSettingsManifest(PackageRelativeManifestFile = @"UnifiedSettings\csharpSettings.registration.json")] [ProvideService(typeof(ICSharpTempPECompilerService), IsAsyncQueryable = false, IsCacheable = true, IsFreeThreaded = true, ServiceName = "C# TempPE Compiler Service")] +// ICSharpProjectHost requests the language service under the covers, and since that needs the UI thread to create a COM aggregation wrapper, this cannot be free-threaded either +[ProvideService(typeof(ICSharpProjectHost), IsAsyncQueryable = true, IsCacheable = true, IsFreeThreaded = false, ServiceName = nameof(ICSharpProjectHost))] [Guid(Guids.CSharpPackageIdString)] internal sealed class CSharpPackage : AbstractPackage, IVsUserSettingsQuery { @@ -75,7 +75,17 @@ private Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks package { var workspace = this.ComponentModel.GetService(); return new TempPECompilerService(workspace.Services.GetService()); - }); + }, promote: true); + + // Historically, the ICSharpProjectHost was fetched by calling QueryService for the CSharpLanguage service, + // and then casting it to ICSharpProjectHost. We keep that mechanism in place, but we now expose ICSharpProjectHost + // as it's own service directly, so that way the request for it is clear and we can do preloading as needed. + AddService(typeof(ICSharpProjectHost), (_, cancellationToken, _) => + { + PreloadProjectSystemComponents(); + return GetServiceAsync(typeof(CSharpLanguageService), swallowExceptions: false); + }, + promote: true); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, ErrorSeverity.General)) { diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs index 44484a483a7..653985f86f5 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/Formatting/StyleViewModel.cs @@ -2227,26 +2227,26 @@ internal StyleViewModel(OptionStore optionStore, IServiceProvider serviceProvide var usingDirectivePlacementPreferences = new List { - new CodeStylePreference(CSharpVSResources.Inside_namespace, isChecked: false), - new CodeStylePreference(CSharpEditorResources.Outside_namespace, isChecked: false), + new(CSharpVSResources.Inside_namespace, isChecked: false), + new(CSharpEditorResources.Outside_namespace, isChecked: false), }; var qualifyMemberAccessPreferences = new List { - new CodeStylePreference(CSharpVSResources.Prefer_this, isChecked: true), - new CodeStylePreference(CSharpVSResources.Do_not_prefer_this, isChecked: false), + new(CSharpVSResources.Prefer_this, isChecked: true), + new(CSharpVSResources.Do_not_prefer_this, isChecked: false), }; var predefinedTypesPreferences = new List { - new CodeStylePreference(ServicesVSResources.Prefer_predefined_type, isChecked: true), - new CodeStylePreference(ServicesVSResources.Prefer_framework_type, isChecked: false), + new(ServicesVSResources.Prefer_predefined_type, isChecked: true), + new(ServicesVSResources.Prefer_framework_type, isChecked: false), }; var typeStylePreferences = new List { - new CodeStylePreference(CSharpVSResources.Prefer_var, isChecked: true), - new CodeStylePreference(CSharpVSResources.Prefer_explicit_type, isChecked: false), + new(CSharpVSResources.Prefer_var, isChecked: true), + new(CSharpVSResources.Prefer_explicit_type, isChecked: false), }; CodeStyleItems.Add(new BooleanCodeStyleOptionViewModel(CodeStyleOptions2.QualifyFieldAccess, CSharpVSResources.Qualify_field_access_with_this, s_fieldDeclarationPreviewTrue, s_fieldDeclarationPreviewFalse, this, optionStore, qualifyGroupTitle, qualifyMemberAccessPreferences)); @@ -2370,9 +2370,9 @@ private void AddBracesOptions(OptionStore optionStore, string bracesPreferenceGr { var bracesPreferences = new List { - new CodeStylePreference(EditorFeaturesResources.Yes, isChecked: false), - new CodeStylePreference(EditorFeaturesResources.No, isChecked: false), - new CodeStylePreference(CSharpVSResources.When_on_multiple_lines, isChecked: false), + new(EditorFeaturesResources.Yes, isChecked: false), + new(EditorFeaturesResources.No, isChecked: false), + new(CSharpVSResources.When_on_multiple_lines, isChecked: false), }; var enumValues = new[] { PreferBracesPreference.Always, PreferBracesPreference.None, PreferBracesPreference.WhenMultiline }; @@ -2389,8 +2389,8 @@ private void AddNamespaceDeclarationsOptions(OptionStore optionStore, string gro { var preferences = new List { - new CodeStylePreference(CSharpVSResources.Block_scoped, isChecked: false), - new CodeStylePreference(CSharpVSResources.File_scoped, isChecked: false), + new(CSharpVSResources.Block_scoped, isChecked: false), + new(CSharpVSResources.File_scoped, isChecked: false), }; var enumValues = new[] { NamespaceDeclarationPreference.BlockScoped, NamespaceDeclarationPreference.FileScoped }; @@ -2407,9 +2407,9 @@ private void AddExpressionBodyOptions(OptionStore optionStore, string expression { var expressionBodyPreferences = new List { - new CodeStylePreference(ServicesVSResources.Never, isChecked: false), - new CodeStylePreference(CSharpVSResources.When_possible, isChecked: false), - new CodeStylePreference(CSharpVSResources.When_on_single_line, isChecked: false), + new(ServicesVSResources.Never, isChecked: false), + new(CSharpVSResources.When_possible, isChecked: false), + new(CSharpVSResources.When_on_single_line, isChecked: false), }; var enumValues = new[] { ExpressionBodyPreference.Never, ExpressionBodyPreference.WhenPossible, ExpressionBodyPreference.WhenOnSingleLine }; @@ -2475,8 +2475,8 @@ private void AddUnusedValueOptions(OptionStore optionStore, string expressionPre { var unusedValuePreferences = new List { - new CodeStylePreference(ServicesVSResources.Unused_local, isChecked: false), - new CodeStylePreference(CSharpVSResources.Discard, isChecked: true), + new(ServicesVSResources.Unused_local, isChecked: false), + new(CSharpVSResources.Discard, isChecked: true), }; var enumValues = new[] diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPage.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPage.cs deleted file mode 100644 index 20e29e2bb96..00000000000 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPage.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using System.Runtime.InteropServices; -using Microsoft.VisualStudio.LanguageServices.Implementation.Options; - -namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options; - -[Guid(Guids.CSharpOptionPageIntelliSenseIdString)] -internal sealed class IntelliSenseOptionPage : AbstractOptionPage -{ - protected override AbstractOptionPageControl CreateOptionPage(IServiceProvider serviceProvider, OptionStore optionStore) - => new IntelliSenseOptionPageControl(optionStore); -} diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml deleted file mode 100644 index e1072c383a6..00000000000 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml.cs deleted file mode 100644 index 55da8747015..00000000000 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageControl.xaml.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System.Windows; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor.CSharp.CompleteStatement; -using Microsoft.VisualStudio.LanguageServices.Implementation.Options; - -namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options; - -internal sealed partial class IntelliSenseOptionPageControl : AbstractOptionPageControl -{ - public IntelliSenseOptionPageControl(OptionStore optionStore) : base(optionStore) - { - InitializeComponent(); - - BindToOption(Show_completion_list_after_a_character_is_typed, CompletionOptionsStorage.TriggerOnTypingLetters, LanguageNames.CSharp); - BindToOption(Show_completion_list_after_a_character_is_deleted, CompletionOptionsStorage.TriggerOnDeletion, LanguageNames.CSharp, onNullValue: static () => false); - Show_completion_list_after_a_character_is_deleted.IsEnabled = Show_completion_list_after_a_character_is_typed.IsChecked == true; - - BindToOption(Automatically_show_completion_list_in_argument_lists, CompletionOptionsStorage.TriggerInArgumentLists, LanguageNames.CSharp); - BindToOption(Highlight_matching_portions_of_completion_list_items, CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems, LanguageNames.CSharp); - BindToOption(Show_completion_item_filters, CompletionViewOptionsStorage.ShowCompletionItemFilters, LanguageNames.CSharp); - - BindToOption(Automatically_complete_statement_on_semicolon, CompleteStatementOptionsStorage.AutomaticallyCompleteStatementOnSemicolon); - BindToOption(Never_include_snippets, CompletionOptionsStorage.SnippetsBehavior, SnippetsRule.NeverInclude, LanguageNames.CSharp); - BindToOption(Always_include_snippets, CompletionOptionsStorage.SnippetsBehavior, SnippetsRule.AlwaysInclude, LanguageNames.CSharp); - BindToOption(Include_snippets_when_question_Tab_is_typed_after_an_identifier, CompletionOptionsStorage.SnippetsBehavior, SnippetsRule.IncludeAfterTypingIdentifierQuestionTab, LanguageNames.CSharp); - SetSnippetsDefaultBehavior(); - - BindToOption(Never_add_new_line_on_enter, CompletionOptionsStorage.EnterKeyBehavior, EnterKeyRule.Never, LanguageNames.CSharp); - BindToOption(Only_add_new_line_on_enter_with_whole_word, CompletionOptionsStorage.EnterKeyBehavior, EnterKeyRule.AfterFullyTypedWord, LanguageNames.CSharp); - BindToOption(Always_add_new_line_on_enter, CompletionOptionsStorage.EnterKeyBehavior, EnterKeyRule.Always, LanguageNames.CSharp); - SetEnterKeyDefaultBehavior(); - - BindToOption(Show_name_suggestions, CompletionOptionsStorage.ShowNameSuggestions, LanguageNames.CSharp); - - BindToOption(Show_items_from_unimported_namespaces, CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.CSharp, onNullValue: static () => true); - - BindToOption(Tab_twice_to_insert_arguments, CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, LanguageNames.CSharp, onNullValue: static () => false); - BindToOption(Show_new_snippet_experience, CompletionOptionsStorage.ShowNewSnippetExperienceUserOption, LanguageNames.CSharp, - onNullValue: () => this.OptionStore.GetOption(CompletionOptionsStorage.ShowNewSnippetExperienceFeatureFlag)); - } - - private void SetSnippetsDefaultBehavior() - { - var snippetValue = this.OptionStore.GetOption(CompletionOptionsStorage.SnippetsBehavior, LanguageNames.CSharp); - if (snippetValue == SnippetsRule.Default) - { - this.Always_include_snippets.IsChecked = true; - } - } - - private void SetEnterKeyDefaultBehavior() - { - var enterKeyBehavior = this.OptionStore.GetOption(CompletionOptionsStorage.EnterKeyBehavior, LanguageNames.CSharp); - if (enterKeyBehavior == EnterKeyRule.Default) - { - this.Never_add_new_line_on_enter.IsChecked = true; - } - } - - private void Show_completion_list_after_a_character_is_typed_Checked(object sender, RoutedEventArgs e) - => Show_completion_list_after_a_character_is_deleted.IsEnabled = Show_completion_list_after_a_character_is_typed.IsChecked == true; - - private void Show_completion_list_after_a_character_is_typed_Unchecked(object sender, RoutedEventArgs e) - { - Show_completion_list_after_a_character_is_deleted.IsEnabled = Show_completion_list_after_a_character_is_typed.IsChecked == true; - Show_completion_list_after_a_character_is_deleted.IsChecked = false; - } -} diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageStrings.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageStrings.cs deleted file mode 100644 index ad680b6c7fe..00000000000 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Options/IntelliSenseOptionPageStrings.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options; - -internal static class IntelliSenseOptionPageStrings -{ - public static string Option_Show_completion_list_after_a_character_is_typed - => ServicesVSResources._Show_completion_list_after_a_character_is_typed; - - public static string Option_Show_completion_list_after_a_character_is_deleted - => ServicesVSResources.Show_completion_list_after_a_character_is__deleted; - - public static string Option_Completion - { - get { return CSharpVSResources.Completion; } - } - - public static string Option_SelectionInCompletionList - { - get { return CSharpVSResources.Selection_In_Completion_List; } - } - - public static string Option_ShowKeywords - { - get { return CSharpVSResources.Place_keywords_in_completion_lists; } - } - - public static string Option_ShowSnippets - { - get { return CSharpVSResources.Place_code_snippets_in_completion_lists; } - } - - public static string Option_Highlight_matching_portions_of_completion_list_items - => ServicesVSResources._Highlight_matching_portions_of_completion_list_items; - - public static string Option_Show_completion_item_filters - => ServicesVSResources.Show_completion_item__filters; - - public static string Option_Automatically_complete_statement_on_semicolon => CSharpVSResources.Automatically_complete_statement_on_semicolon; - - public static string Enter_key_behavior_Title - => ServicesVSResources.Enter_key_behavior_colon; - - public static string Option_Never_add_new_line_on_enter - => ServicesVSResources.Never_add_new_line_on_enter; - - public static string Option_Only_add_new_line_on_enter_with_whole_word - => ServicesVSResources._Only_add_new_line_on_enter_after_end_of_fully_typed_word; - - public static string Option_Always_add_new_line_on_enter - => ServicesVSResources.Always_add_new_line_on_enter; - - public static string Snippets_behavior - => ServicesVSResources.Snippets_behavior; - - public static string Option_Never_include_snippets - => ServicesVSResources.Never_include_snippets; - - public static string Option_Always_include_snippets - => CSharpVSResources.Always_include_snippets; - - public static string Option_Include_snippets_when_question_Tab_is_typed_after_an_identifier - => CSharpVSResources.Include_snippets_when_Tab_is_typed_after_an_identifier; - - public static string Option_Show_name_s_uggestions - => CSharpVSResources.Show_name_s_uggestions; - - public static string Option_Show_items_from_unimported_namespaces - => ServicesVSResources.Show_items_from_unimported_namespaces; - - public static string Option_Tab_twice_to_insert_arguments_experimental - => ServicesVSResources.Tab_twice_to_insert_arguments_experimental; - - public static string Automatically_show_completion_list_in_argument_lists - => CSharpVSResources.Automatically_show_completion_list_in_argument_lists; - - public static string Option_Show_new_snippet_experience_experimental - => CSharpVSResources.Show_new_snippet_experience_experimental; -} diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef b/src/roslyn/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef index 6de5ab0d674..42425056b5a 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef @@ -34,6 +34,20 @@ "Name"="C# Language Service" "IsAsyncQueryable"=dword:00000001 +[$RootKey$\Services\{1F3B9583-A66A-4be1-A15B-901DA4DB4ACF}] +@="{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" +"Name"="ICSharpProjectHost" +"IsAsyncQueryable"=dword:00000001 +"IsCacheable"=dword:00000001 +"IsFreeThreaded"=dword:00000000 + +[$RootKey$\Services\{dba64c84-56df-4e20-8aa6-02332a97f474}] +@="{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" +"Name"="C# TempPE Compiler Service" +"IsAsyncQueryable"=dword:00000000 +"IsCacheable"=dword:00000001 +"IsFreeThreaded"=dword:00000001 + [$RootKey$\AutomationProperties\TextEditor\CSharp-Specific] @="#104" "Description"="#105" @@ -118,12 +132,6 @@ "Page"="{eae577a7-acb9-40f5-a7b1-d2878c3c7d6f}" "Keywords"="#313" -[$RootKey$\Languages\Language Services\CSharp\EditorToolsOptions\IntelliSense] -@="#103" -"Package"="{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" -"Page"="{ede66829-7a36-4c5d-8e20-9290195dcf80}" -"Keywords"="#312" - [$RootKey$\Languages\Language Services\CSharp\EditorToolsOptions\Code Style\Formatting] @="#107" "Package"="{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" @@ -193,4 +201,4 @@ [$RootKey$\SettingsManifests\{13c3bbb4-f18f-4111-9f54-a0fb010d9194}] @="Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService.CSharpPackage" "ManifestPath"="$PackageFolder$\UnifiedSettings\csharpSettings.registration.json" -"CacheTag"=qword:18C11F0A543B8AD0 +"CacheTag"=qword:E022D0014F819ADD diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs b/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs index 2fee5a72500..ca069b3a27e 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/Snippets/CSharpSnippetExpansionLanguageHelper.cs @@ -88,7 +88,7 @@ public override async Task AddImportsAsync( // In Venus/Razor, inserting imports statements into the subject buffer does not work. // Instead, we add the imports through the contained language host. - if (TryAddImportsToContainedDocument(document, newUsingDirectives.Where(u => u.Alias == null).Select(u => u.Name!.ToString()))) + if (TryAddImportsToContainedDocument(document, newUsingDirectives.SelectAsArray(u => u.Alias == null, u => u.Name!.ToString()))) return document; var addImportService = document.GetRequiredLanguageService(); diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/UnifiedSettings/csharpSettings.registration.json b/src/roslyn/src/VisualStudio/CSharp/Impl/UnifiedSettings/csharpSettings.registration.json index 627bd2ab304..724ff5e9eff 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/UnifiedSettings/csharpSettings.registration.json +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/UnifiedSettings/csharpSettings.registration.json @@ -5,7 +5,7 @@ { "properties": { // CompletionOptionsStorage.TriggerOnTypingLetters - "textEditor.csharp.intellisense.triggerCompletionOnTypingLetters": { + "languages.csharp.intellisense.triggerCompletionOnTypingLetters": { "title": "@Show_completion_list_after_a_character_is_typed;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -20,12 +20,12 @@ } }, // CompletionOptionsStorage.TriggerOnDeletion - "textEditor.csharp.intellisense.triggerCompletionOnDeletion": { + "languages.csharp.intellisense.triggerCompletionOnDeletion": { "title": "@Show_completion_list_after_a_character_is_deleted;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": false, "order": 1, - "enableWhen": "${config:textEditor.csharp.intellisense.triggerCompletionOnTypingLetters}=='true'", + "enableWhen": "${config:languages.csharp.intellisense.triggerCompletionOnTypingLetters}=='true'", "migration": { "pass": { "input": { @@ -36,7 +36,7 @@ } }, // CompletionOptionsStorage.TriggerInArgumentLists - "textEditor.csharp.intellisense.triggerCompletionInArgumentLists": { + "languages.csharp.intellisense.triggerCompletionInArgumentLists": { "title": "@Automatically_show_completion_list_in_argument_lists;..\\Microsoft.VisualStudio.LanguageServices.CSharp.dll", "type": "boolean", "default": true, @@ -51,7 +51,7 @@ } }, // CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems - "textEditor.csharp.intellisense.highlightMatchingPortionsOfCompletionListItems": { + "languages.csharp.intellisense.highlightMatchingPortionsOfCompletionListItems": { "title": "@Highlight_matching_portions_of_completion_list_items;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -66,7 +66,7 @@ } }, // CompletionViewOptionsStorage.ShowCompletionItemFilters - "textEditor.csharp.intellisense.showCompletionItemFilters": { + "languages.csharp.intellisense.showCompletionItemFilters": { "title": "@Show_completion_item_filters;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -81,7 +81,7 @@ } }, // CompleteStatementOptionsStorage.AutomaticallyCompleteStatementOnSemicolon - "textEditor.csharp.intellisense.completeStatementOnSemicolon": { + "languages.csharp.intellisense.completeStatementOnSemicolon": { "title": "@Automatically_complete_statement_on_semicolon;..\\Microsoft.VisualStudio.LanguageServices.CSharp.dll", "type": "boolean", "default": true, @@ -96,7 +96,7 @@ } }, // CompletionOptionsStorage.SnippetsBehavior - "textEditor.csharp.intellisense.snippetsBehavior": { + "languages.csharp.intellisense.snippetsBehavior": { "title": "@Snippets_behavior;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "string", "enum": [ "neverInclude", "alwaysInclude", "includeAfterTypingIdentifierQuestionTab" ], @@ -135,7 +135,7 @@ } }, // CompletionOptionsStorage.EnterKeyBehavior - "textEditor.csharp.intellisense.returnKeyCompletionBehavior": { + "languages.csharp.intellisense.returnKeyCompletionBehavior": { "title": "@Enter_key_behavior;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "string", "enum": [ "never", "afterFullyTypedWord", "always" ], @@ -174,7 +174,7 @@ } }, // CompletionOptionsStorage.ShowNameSuggestions - "textEditor.csharp.intellisense.showNameCompletionSuggestions": { + "languages.csharp.intellisense.showNameCompletionSuggestions": { "title": "@Show_name_suggestions;..\\Microsoft.VisualStudio.LanguageServices.CSharp.dll", "type": "boolean", "default": true, @@ -189,7 +189,7 @@ } }, // CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces - "textEditor.csharp.intellisense.showCompletionItemsFromUnimportedNamespaces": { + "languages.csharp.intellisense.showCompletionItemsFromUnimportedNamespaces": { "title": "@Show_items_from_unimported_namespaces;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -204,7 +204,7 @@ } }, // CompletionViewOptionsStorage.EnableArgumentCompletionSnippets - "textEditor.csharp.intellisense.enableArgumentCompletionSnippets": { + "languages.csharp.intellisense.enableArgumentCompletionSnippets": { "title": "@Tab_twice_to_insert_arguments;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": false, @@ -224,7 +224,7 @@ } }, // CompletionOptionsStorage.ShowNewSnippetExperienceUserOption - "textEditor.csharp.intellisense.showNewSnippetExperience": { + "languages.csharp.intellisense.showNewSnippetExperience": { "title": "@Show_new_snippet_experience;..\\Microsoft.VisualStudio.LanguageServices.CSharp.dll", "type": "boolean", "default": false, @@ -250,12 +250,11 @@ } }, "categories": { - "textEditor.csharp":{ + "languages.csharp":{ "title": "@101;{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" }, - "textEditor.csharp.intellisense": { - "title": "@103;{13c3bbb4-f18f-4111-9f54-a0fb010d9194}", - "legacyOptionPageId": "EDE66829-7A36-4c5d-8E20-9290195DCF80" + "languages.csharp.intellisense": { + "title": "@103;{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" } } } diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/VSPackage.resx b/src/roslyn/src/VisualStudio/CSharp/Impl/VSPackage.resx index 46dd42a04b2..008df0dd5b0 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/VSPackage.resx +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/VSPackage.resx @@ -283,26 +283,6 @@ Insert space before and after binary operators; Change formatting options for wrapping;leave block on single line;leave statements and member declarations on the same line C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - C# IntelliSense options page keywords - Formatting "Formatting" category node under Tools > Options, Text Editor, C#, Code Style (no corresponding keywords) diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf index 491d2bb0761..09e6d01f567 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf @@ -298,43 +298,6 @@ Vložit mezeru před a za binární operátory; Změnit možnosti formátování pro obtékání;ponechat blok na stejném řádku;ponechat příkazy a deklarace členu na stejném řádku C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Změnit nastavení pro seznam dokončení;Předvolit naposledy použitou položku; Seznamy dokončení; -Po zadání znaku zobrazit seznam dokončení; -Po odstranění znaku zobrazit seznam dokončení; -Automaticky zobrazovat seznam dokončení v seznamech argumentů (experimentální); -Zvýraznit odpovídající části položek seznamu dokončení; -Zobrazit filtry položek dokončení; -Automaticky dokončit příkaz středníkem; -Chování fragmentů; -Nikdy nezahrnovat fragmenty; -Vždy zahrnovat fragmenty; -Zahrnovat fragmenty po zadání ?-Tab za identifikátor; -Chování klávesy Enter; -Při stisknutí klávesy Enter nikdy nepřidávat nový řádek; -Při stisknutí klávesy Enter přidat nový řádek jenom po dopsání celého slova; -Při stisknutí klávesy Enter vždy přidat nový řádek; -Zobrazovat návrhy názvů; -Zobrazit položky z neimportovaných oborů názvů (experimentální); - C# IntelliSense options page keywords - Formatting Formátování diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf index 855c18c57f7..2c2223db705 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf @@ -298,43 +298,6 @@ Leerzeichen vor und nach binären Operatoren einfügen; Formatierungsoptionen für Umbruch ändern;Block auf einzelner Zeile belassen;Anweisungen und Memberdeklarationen auf der gleichen Zeile belassen C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Einstellungen der Vervollständigungsliste ändern;Vorauswahl des zuletzt verwendeten Members; Vervollständigungslisten; -Vervollständigungsliste nach Eingabe eines Zeichens anzeigen; -Vervollständigungsliste nach Löschen eines Zeichens anzeigen; -Vervollständigungsliste in Argumentlisten automatisch anzeigen (experimentell); -\Übereinstimmende Teile der Vervollständigungslistenelemente anzeigen; -Vervollständigungselementfilter anzeigen; -Anweisung bei Semikolon automatisch abschließen; -Schnipselverhalten; -Schnipsel nie einschließen; -Schnipsel immer einschließen; -Schnipsel einschließen, wenn ?-TAB nach einem Bezeichner eingegeben wird; -Verhalten der EINGABETASTE; -Nie neue Zeile beim Drücken der EINGABETASTE einfügen; -Neue Zeile beim Drücken der EINGABETASTE nur nach einem vollständig eingegebenen Wort einfügen; -Immer neue Zeile beim Drücken der EINGABETASTE einfügen; -Namensvorschläge anzeigen; -Elemente aus nicht importierten Namespaces anzeigen (experimentell); - C# IntelliSense options page keywords - Formatting Formatierung diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf index 7d52d75a83e..cbdb0f15cd4 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf @@ -298,43 +298,6 @@ Insertar espacio delante y detrás de operadores binarios; Cambiar opciones de formato de ajuste;Mantener bloque en una sola línea;Mantener instrucciones y declaraciones de miembros en la misma línea C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Cambiar configuración de la lista de finalización;Preseleccionar el miembro usado recientemente; Listas de finalización; -Mostrar lista de finalización después de escribir un carácter; -Mostrar lista de finalización después de eliminar un carácter; -Mostrar lista de finalización en las listas de argumentos automáticamente (experimental); -Resaltar partes coincidentes de elementos de la lista de finalización; -Mostrar filtros de elementos de finalización; -Completar automáticamente la instrucción al introducir punto y coma; -Comportamiento de los fragmentos de código; -No incluir nunca fragmentos de código; -Incluir siempre fragmentos de código; -Incluir fragmentos de código cuando ?-Tab se escriba después de un identificador; -Comportamiento de la tecla Entrar; -No agregar nunca una línea nueva al presionar Entrar; -Agregar solo una nueva línea con Entrar al final de palabras; -Agregar siempre una línea nueva al presionar Entrar; -Mostrar sugerencias de nombre; -Mostrar elementos de espacios de nombres no importados (experimental); - C# IntelliSense options page keywords - Formatting Formato diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf index 3cc99bbae9a..9fd47da821b 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf @@ -298,43 +298,6 @@ Insérer un espace avant et après les opérateurs binaires ; Changer les options de mise en forme pour l'enveloppement ; Laisser un bloc sur une seule ligne ; Laisser les instructions et les déclarations de membre sur la même ligne C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Changer les paramètres de la liste de complétion;Présélectionner le dernier membre utilisé récemment;Listes de complétion; -Afficher la liste de complétion une fois qu'un caractère a été tapé ; -Afficher la liste de complétion après la suppression d'un caractère ; -Afficher automatiquement la liste de complétion dans les listes d'arguments (expérimental) ; -Mettre en surbrillance les parties correspondantes des éléments de liste de complétion ; -Afficher les filtres d'éléments de complétion ; -Effectuer la complétion automatique de l'instruction après l'entrée d'un point-virgule ; -Comportement des extraits ; -Ne jamais inclure d'extraits ; -Toujours inclure les extraits ; -Inclure les extraits quand ?-Tab est tapé après un identificateur ; -Comportement de la touche Entrée ; -Ne jamais ajouter de nouvelle ligne après Entrée ; -Ajouter uniquement une nouvelle ligne une fois que la touche Entrée a été enfoncée à la fin du mot complet tapé ; -Toujours ajouter une nouvelle ligne après Entrée ; -Afficher les suggestions de nom ; -Afficher les éléments des espaces de noms qui ne sont pas importés (expérimental) ; - C# IntelliSense options page keywords - Formatting Mise en forme diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf index 9d2070b4e61..21625d7e7f7 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf @@ -298,43 +298,6 @@ Inserisci spazio prima e dopo gli operatori binari; Modifica opzioni di formattazione per ritorno a capo;Lascia blocco su una sola riga;Lascia istruzioni e dichiarazioni di membri sulla stessa riga C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Modifica impostazioni dell'elenco di completamento;Preseleziona membri usati di recente; Elenchi di completamento; -Mostra elenco di completamento dopo la digitazione di un carattere; -Mostra elenco di completamento dopo l'eliminazione di un carattere; -Mostra automaticamente l'elenco di completamento negli elenchi di argomenti (sperimentale); -Evidenzia le parti corrispondenti di voci dell'elenco di completamento; -Mostra i filtri per le voci di completamento; -Completa automaticamente istruzione dopo punto e virgola; -Comportamento dei frammenti; -Non includere mai i frammenti; -Includi sempre i frammenti; -Includi i frammenti quando si digita ?+TAB dopo un identificatore; -Comportamento del tasto INVIO; -Non aggiungere mai una nuova riga dopo INVIO; -Aggiungi una nuova riga dopo INVIO solo alla fine della parola digitata; -Aggiungi sempre una nuova riga dopo INVIO; -Mostra suggerimenti per nomi; -Mostra elementi da spazi dei nomi non importati (sperimentale); - C# IntelliSense options page keywords - Formatting Formattazione diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf index 5e7e20dfe29..43d00118dd4 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf @@ -298,43 +298,6 @@ for ステートメントで、セミコロンの前にスペースを挿入す 折り返しの書式オプションを変更する;ブロックを単一行に配置する;1 行に複数のステートメントとメンバー宣言を表示する C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - 入力候補一覧の設定を変更する;最近使用されたメンバーをあらかじめ選択する; 入力候補一覧; -文字が入力された後に入力候補一覧を表示する; -文字が削除された後に入力候補一覧を表示する; -引数リストに入力候補一覧を自動的に表示する (試験段階); -入力候補一覧の項目の一致している部分を強調表示する; -入力候補の項目フィルターを表示する; -セミコロンでステートメントをオートコンプリートに設定する; -スニペットの動作; -スニペットを含めない; -常にスニペットを含める; -識別子の後に ? Tab を入力したときにスニペットを含める; -Enter キー入力時動作; -Enter キーで新しい行を追加しない; -単語を完全に入力した後のみ、Enter キーで新しい行を追加する; -Enter キーで常に新しい行を追加する; -名前の提案を表示する; -未インポートの名前空間からの項目を表示する (試験段階); - C# IntelliSense options page keywords - Formatting 書式設定 diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf index 06538bb7e1f..9403984fae2 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf @@ -298,43 +298,6 @@ for 문의 세미콜론 앞에 공백 삽입; 래핑 서식 옵션 변경;블록 한 줄에 두기;문과 멤버 선언을 같은 줄에 두기 C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - 완성 목록 설정 변경;가장 최근에 사용한 멤버 미리 선택; 완성 목록; -문자를 입력하면 완성 목록 표시; -문자를 삭제하면 완성 목록 표시; -인수 목록에 자동으로 완성 목록 표시(실험적); -완성 목록 항목에서 일치하는 부분 강조 표시; -완성 항목 필터 표시; -세미콜론 입력 시 자동으로 문 완성; -코드 조각 동작; -코드 조각 포함 안 함; -코드 조각 항상 포함; -식별자 뒤에 ?-Tab을 입력하면 코드 조각 포함; -<Enter> 키 동작; -<Enter> 키를 누를 때 새 줄 추가 안 함; -단어를 모두 입력한 후 <Enter> 키를 누를 때만 새 줄 추가; -<Enter> 키를 누를 때 항상 새 줄 추가; -이름 제안 표시; -가져오지 않은 네임스페이스의 항목 표시(실험적); - C# IntelliSense options page keywords - Formatting 서식 diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf index d2e5a36d2d2..a708d46c14e 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf @@ -298,42 +298,6 @@ Wstaw spację przed operatorami binarnymi i po nich; Zmień opcje formatowania dla zawijania;pozostaw blok w pojedynczym wierszu;pozostaw instrukcje i deklaracje składowych w tym samym wierszu C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Zmień ustawienia listy uzupełniania;Wybierz wstępnie ostatnio używaną składową; Listy uzupełniania; -Pokaż listę uzupełniania po wpisaniu znaku; -Pokaż listę uzupełniania po usunięciu znaku; -Automatycznie pokazuj listę uzupełniania na liście argumentów (funkcja eksperymentalna); -Wyróżnij pasujące fragmenty elementów listy uzupełniania; -Pokaż filtry elementów uzupełniania; -Zachowanie fragmentów kodu; -Nigdy nie dołączaj fragmentów kodu; -Zawsze dołączaj fragmenty kodu; -Dołącz fragmenty kodu po wpisaniu znaku ? po identyfikatorze i naciśnięciu klawisza Tab; -Zachowanie klawisza Enter; -Nigdy nie dodawaj nowego wiersza po naciśnięciu klawisza Enter; -Dodaj nowy wiersz po naciśnięciu klawisza Enter tylko po zakończeniu pełnego wpisanego wyrazu; -Zawsze dodawaj nowy wiersz po naciśnięciu klawisza Enter; -Pokaż sugestie dotyczące nazwy; -Pokaż elementy z niezaimportowanych przestrzeni nazw (funkcja eksperymentalna); - C# IntelliSense options page keywords - Formatting Formatowanie diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf index 58058a14990..7e76b6d1636 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf @@ -298,43 +298,6 @@ Inserir espaço antes e depois dos operadores binários; Alterar opções de formatação para quebra automática; deixar bloco na linha; deixar instruções e declarações de membro na mesma linha C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Alterar as configurações da lista de conclusão;Pré-selecionar o membro usado mais recentemente; Listas de Conclusão; -Mostrar a lista de conclusão após a digitação de um caractere; -Mostrar a lista de conclusão após a exclusão de um caractere; -Mostrar automaticamente a lista de conclusão nas listas de argumentos (experimental); -Realçar as partes correspondentes dos itens da lista de conclusão; -Mostrar os filtros de itens de conclusão; -Concluir a instrução automaticamente no ponto e vírgula; -Comportamento de snippets; -Nunca incluir snippets; -Sempre incluir snippets; -Incluir snippets quando ?-Tab é digitado após um identificador; -Comportamento da tecla Enter; -Nunca adicionar uma nova linha ao pressionar Enter; -Apenas adicionar uma nova linha ao pressionar Enter depois que a palavra é digitada completamente; -Sempre adicionar uma nova linha ao pressionar Enter; -Mostrar sugestões de nomes; -Mostrar itens de namespaces não importados (experimental); - C# IntelliSense options page keywords - Formatting Formatação diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf index 5e3a96779ae..4dcc228d4ca 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf @@ -298,43 +298,6 @@ Insert space before and after binary operators; Изменение параметров форматирования для переноса по словам; оставить блок в одной строке; оставить запросы и объявления членов в той же строке C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Изменить параметры списка завершения;Предварительно выбрать наиболее часто используемый элемент; Списки завершения; -Показывать список завершения после ввода символа; -Показывать список завершения после удаления символа; -Автоматически показывать список завершения в списках аргументов (экспериментальная функция); -Выделять совпадающие части элементов списка завершения; -Показывать фильтры элементов завершения; -Автоматически завершать предложение при вводе точки с запятой; -Поведение фрагментов кода; -Никогда не включать фрагменты кода; -Всегда включать фрагменты кода; -Включать фрагменты кода, когда после идентификатора указывается ?-Tab; -Поведение при нажатии клавиши ВВОД; -Никогда не добавлять новую строку при нажатии клавиши ВВОД; -Добавлять новую строку при нажатии клавиши ВВОД только в конце полностью введенного слова; -Всегда добавлять новую строку при нажатии клавиши ВВОД; -Показывать варианты имен; -Показывать элементы из неимпортированных пространств имен (экспериментальная функция); - C# IntelliSense options page keywords - Formatting Форматирование diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf index 5605ea2851e..41f391352b4 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf @@ -298,43 +298,6 @@ for deyiminde noktalı virgülden önce boşluk ekle; Sarmalama için biçimlendirme seçeneklerini değiştir;bloğu tek satırda bırak;ifadeleri ve üye bildirimlerini aynı satırda bırak C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - Tamamlama listesi ayarlarını değiştir;Son kullanılan üyeyi önceden seç; Tamamlama Listeleri; -Bir karakter yazıldıktan sonra tamamlama listesini göster; -Bir karakter silindikten sonra tamamlama listesini göster; -Bağımsız değişken listelerinde tamamlama listelerini otomatik olarak göster (deneysel); -Tamamlama listesi öğelerinin eşleşen kısımlarını vurgula; -Tamamlama öğesi filtrelerini göster; -Deyimi noktalı virgülle otomatik olarak tamamla; -Kod parçacığı davranışı; -Kod parçacıklarını hiçbir zaman dahil etme; -Kod parçacıklarını her zaman dahil et; -Bir tanımlayıcıdan sonra ?- yazılıp Sekme tuşuna basılırsa kod parçacıklarını dahil et; -Enter tuşu davranışı; -Enter tuşuna basıldığında hiçbir zaman yeni satır ekleme; -Enter tuşuna basıldığında yalnızca tam bir kelime yazılmışsa sonuna yeni satır ekle; -Enter tuşuna basıldığında her zaman yeni satır ekle; -Ad önerilerini göster; -İçeri aktarılmamış ad alanlarındaki öğeleri göster (deneysel); - C# IntelliSense options page keywords - Formatting Biçimlendirme diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf index 699faaf0238..f418b76da38 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf @@ -298,43 +298,6 @@ Insert space before and after binary operators; 更改换行的格式选项;将块保留在一行上;将语句和成员声明保留在同一行上 C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - 更改完成列表设置;预先选择最近使用过的成员;完成列表; -在键入字符后显示完成列表; -在删除字符后显示完成列表; -自动显示参数列表中的完成列表(实验性); -突出显示完成列表项的匹配部分; -显示完成项筛选器; -以分号自动补全语句; -片段行为; -永不包含片段; -总是包含片段; -在标识符后键入 ?-Tab 时包含片段; -输入键行为; -永不在按 Enter 时添加新行; -仅在完整键入的字词结尾后按 Enter 时才添加新行; -总是在按 Enter 时添加新行; -显示名称建议; -显示未导入的命名空间中的项(实验性); - C# IntelliSense options page keywords - Formatting 格式设置 diff --git a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf index 7bf151fe1c9..5354974cc39 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf +++ b/src/roslyn/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf @@ -298,43 +298,6 @@ Insert space before and after binary operators; 變更換行的格式化選項;將區塊保留在同一行;將陳述式與成員宣告保留在同一行 C# Formatting > Wrapping options page keywords - - Change completion list settings;Pre-select most recently used member; Completion Lists; -Show completion list after a character is typed; -Show completion list after a character is deleted; -Automatically show completion list in argument lists (experimental); -Highlight matching portions of completion list items; -Show completion item filters; -Automatically complete statement on semicolon; -Snippets behavior; -Never include snippets; -Always include snippets; -Include snippets when ?-Tab is typed after an identifier; -Enter key behavior; -Never add new line on enter; -Only add new line on enter after end of fully typed word; -Always add new line on enter; -Show name suggestions; -Show items from unimported namespaces (experimental); - 變更自動完成清單設定;預先選取最近使用的成員; 自動完成清單; -在鍵入字元後顯示自動完成清單; -在刪除字元後顯示自動完成清單; -自動在引數清單中顯示自動完成清單 (實驗性); -醒目提示自動完成清單項目的相符部分; -顯示完成項目篩選; -在遇到分號時自動完成陳述式; -程式碼片段行為; -一律不包含程式碼片段; -一律包含程式碼片段; -在識別碼後鍵入 ?-Tab 時包含程式碼片段; -Enter 鍵行為; -一律不在按下 Enter 鍵時新增一行程式碼; -只在按下 Enter 鍵時,於完整鍵入的文字結尾後新增一行程式碼; -一律在按下 Enter 鍵時新增一行程式碼; -顯示名稱建議; -顯示未匯入命名空間中的項目 (實驗性); - C# IntelliSense options page keywords - Formatting 格式化 diff --git a/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs b/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs index 74e0f3482d5..b2da5fa1910 100644 --- a/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs +++ b/src/roslyn/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs @@ -487,7 +487,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKey(Size size, [Combina } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); } @@ -508,7 +508,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocument(Size size, [Combinator } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); } @@ -619,7 +619,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1(Size si } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -643,7 +643,7 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2(Size si } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -662,12 +662,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKey_WriteWithSolutionKe var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); } @@ -683,12 +683,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocument_WriteWithSolutionKey(S var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); } @@ -704,7 +704,7 @@ public async Task TestOpenWithSolutionReadWithDocumentKey_WriteWithSolutionKey(S var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -725,7 +725,7 @@ public async Task TestOpenWithSolutionReadWithDocument_WriteWithSolutionKey(Size var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -746,7 +746,7 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument1_WriteWithS var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -770,7 +770,7 @@ public async Task TestOpenWithSolutionReadWithDocumentKeyAndDocument2_WriteWithS var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } @@ -794,12 +794,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument1_WriteWi var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(DocumentKey.ToDocumentKey(document), streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(DocumentKey.ToDocumentKey(document), streamName1))); @@ -818,12 +818,12 @@ public async Task TestOpenWithSolutionKeyReadWithDocumentKeyAndDocument2_WriteWi var streamName1 = "stream"; { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); await storage.WriteStreamAsync(document, streamName1, EncodeString(GetData1(size)), checksum: s_checksum1); } { - var storage = await GetStorageFromKeyAsync(solution.Workspace.Services, SolutionKey.ToSolutionKey(solution)); + var storage = await GetStorageFromKeyAsync(solution.Services, SolutionKey.ToSolutionKey(solution)); Assert.True(await storage.ChecksumMatchesAsync(document, streamName1, s_checksum1)); Assert.Equal(GetData1(size), ReadStringToEnd(await storage.ReadStreamAsync(document, streamName1))); @@ -1008,7 +1008,7 @@ internal async Task GetStorageAsync( _storageService?.GetTestAccessor().Shutdown(); var configuration = new MockPersistentStorageConfiguration(solution.Id, persistentFolder.Path, throwOnFailure); - _storageService = (AbstractPersistentStorageService)solution.Workspace.Services.SolutionServices.GetPersistentStorageService(); + _storageService = (AbstractPersistentStorageService)solution.Services.GetPersistentStorageService(); var storage = await _storageService.GetStorageAsync( SolutionKey.ToSolutionKey(solution), configuration, faultInjector, CancellationToken.None); @@ -1022,11 +1022,11 @@ internal async Task GetStorageAsync( } internal async Task GetStorageFromKeyAsync( - HostWorkspaceServices services, SolutionKey solutionKey, IPersistentStorageFaultInjector? faultInjector = null) + SolutionServices services, SolutionKey solutionKey, IPersistentStorageFaultInjector? faultInjector = null) { var configuration = new MockPersistentStorageConfiguration(solutionKey.Id, _persistentFolder.Path, throwOnFailure: true); - _storageService = (AbstractPersistentStorageService)services.SolutionServices.GetPersistentStorageService(); + _storageService = (AbstractPersistentStorageService)services.GetPersistentStorageService(); var storage = await _storageService.GetStorageAsync( solutionKey, configuration, faultInjector, CancellationToken.None); diff --git a/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs b/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs index fc34204534f..fb601810abe 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs @@ -279,8 +279,8 @@ internal ParameterConfiguration GetParameterConfiguration() { return new ParameterConfiguration( _originalParameterConfiguration.ThisParameter, - [.. _parametersWithoutDefaultValues.Where(p => !p.IsRemoved).Select(p => p.Parameter)], - [.. _parametersWithDefaultValues.Where(p => !p.IsRemoved).Select(p => p.Parameter)], + _parametersWithoutDefaultValues.SelectAsArray(p => !p.IsRemoved, p => p.Parameter), + _parametersWithDefaultValues.SelectAsArray(p => !p.IsRemoved, p => p.Parameter), (_paramsParameter == null || _paramsParameter.IsRemoved) ? null : (ExistingParameter)_paramsParameter.Parameter, selectedIndex: -1); } diff --git a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs index 1545012d679..bf59c6afe2a 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/IVisualStudioDiagnosticAnalyzerService.cs @@ -22,7 +22,7 @@ internal interface IVisualStudioDiagnosticAnalyzerService /// /// This is used by the Ruleset Editor from ManagedSourceCodeAnalysis.dll in VisualStudio. /// - IReadOnlyDictionary> GetAllDiagnosticDescriptors(IVsHierarchy? hierarchy); + Task>> GetAllDiagnosticDescriptorsAsync(IVsHierarchy? hierarchy, CancellationToken cancellationToken); /// /// Runs all the applicable NuGet and VSIX diagnostic analyzers for the given project OR current solution in background and updates the error list. diff --git a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs index 1ff0c6a715d..08839c96cec 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs @@ -34,7 +34,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; internal sealed partial class VisualStudioDiagnosticAnalyzerService( VisualStudioWorkspace workspace, IVsService statusbar, - DiagnosticAnalyzerInfoCache.SharedGlobalCache diagnosticAnalyzerInfoCache, IThreadingContext threadingContext, IVsHierarchyItemManager vsHierarchyItemManager, IAsynchronousOperationListenerProvider listenerProvider) : IVisualStudioDiagnosticAnalyzerService @@ -45,7 +44,6 @@ internal sealed partial class VisualStudioDiagnosticAnalyzerService( private readonly VisualStudioWorkspace _workspace = workspace; private readonly IVsService _statusbar = statusbar; - private readonly DiagnosticAnalyzerInfoCache _diagnosticAnalyzerInfoCache = diagnosticAnalyzerInfoCache.AnalyzerInfoCache; private readonly IThreadingContext _threadingContext = threadingContext; private readonly IVsHierarchyItemManager _vsHierarchyItemManager = vsHierarchyItemManager; private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService); @@ -69,14 +67,19 @@ public async Task InitializeAsync(IAsyncServiceProvider serviceProvider, Cancell } } - public IReadOnlyDictionary> GetAllDiagnosticDescriptors(IVsHierarchy? hierarchy) + public async Task>> GetAllDiagnosticDescriptorsAsync( + IVsHierarchy? hierarchy, + CancellationToken cancellationToken) { var currentSolution = _workspace.CurrentSolution; var hostAnalyzers = currentSolution.SolutionState.Analyzers; + var diagnosticService = currentSolution.Services.GetRequiredService(); if (hierarchy == null) { - return Transform(hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache)); + return Transform( + await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync( + currentSolution, cancellationToken).ConfigureAwait(false)); } // Analyzers are only supported for C# and VB currently. @@ -88,8 +91,8 @@ public IReadOnlyDictionary> GetAllDiag { var project = projectsWithHierarchy.FirstOrDefault(); return project == null - ? Transform(hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache)) - : Transform(hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache, project)); + ? Transform(await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync(currentSolution, cancellationToken).ConfigureAwait(false)) + : Transform(await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync(project, cancellationToken).ConfigureAwait(false)); } else { @@ -98,7 +101,8 @@ public IReadOnlyDictionary> GetAllDiag var descriptorsMap = ImmutableDictionary.CreateBuilder>(); foreach (var project in projectsWithHierarchy) { - var descriptorsPerReference = hostAnalyzers.GetDiagnosticDescriptorsPerReference(_diagnosticAnalyzerInfoCache, project); + var descriptorsPerReference = await diagnosticService.GetDiagnosticDescriptorsPerReferenceAsync( + project, cancellationToken).ConfigureAwait(false); foreach (var (displayName, descriptors) in descriptorsPerReference) { if (descriptorsMap.TryGetValue(displayName, out var existingDescriptors)) diff --git a/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs b/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs index d6d5e67eaa4..d78de01d96f 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolDataViewModel.cs @@ -48,8 +48,8 @@ public DocumentSymbolDataViewModel( Children = children; } - private static readonly PropertyChangedEventArgs _isExpandedPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsExpanded)); - private static readonly PropertyChangedEventArgs _isSelectedPropertyChangedEventArgs = new PropertyChangedEventArgs(nameof(IsSelected)); + private static readonly PropertyChangedEventArgs _isExpandedPropertyChangedEventArgs = new(nameof(IsExpanded)); + private static readonly PropertyChangedEventArgs _isSelectedPropertyChangedEventArgs = new(nameof(IsSelected)); private void NotifyPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, propertyName switch diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs index 821841df214..35797125575 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Threading; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ExternalAccess.LegacyCodeAnalysis.Api; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; @@ -15,11 +17,13 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.LegacyCodeAnalysis; [Export(typeof(ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor(IVisualStudioDiagnosticAnalyzerService implementation) +internal sealed class LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor( + IThreadingContext threadingContext, + IVisualStudioDiagnosticAnalyzerService implementation) : ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor { public IReadOnlyDictionary> GetAllDiagnosticDescriptors(IVsHierarchy hierarchyOpt) - => implementation.GetAllDiagnosticDescriptors(hierarchyOpt); + => threadingContext.JoinableTaskFactory.Run(() => implementation.GetAllDiagnosticDescriptorsAsync(hierarchyOpt, CancellationToken.None)); public void RunAnalyzers(IVsHierarchy hierarchyOpt) => implementation.RunAnalyzers(hierarchyOpt); diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs index b093f5376f2..0c6c2be2c7c 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs @@ -22,8 +22,8 @@ internal sealed partial class VisualStudioGlobalOperationNotificationService : A [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public VisualStudioGlobalOperationNotificationService( - IThreadingContext threadingContext, - IAsynchronousOperationListenerProvider listenerProvider) : base(listenerProvider, threadingContext.DisposalToken) + IThreadingContext threadingContext, + IAsynchronousOperationListenerProvider listenerProvider) : base(listenerProvider, threadingContext.DisposalToken) { _solutionEventMonitor = new SolutionEventMonitor(this); diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs index 2d6909e1378..63213cc907c 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/VSTypeScriptVisualStudioProjectWrapper.LSPContainedDocumentServiceProvider.cs @@ -17,7 +17,7 @@ private LspContainedDocumentServiceProvider() _documentPropertiesService = VirtualDocumentPropertiesService.Instance; } - public static LspContainedDocumentServiceProvider Instance = new LspContainedDocumentServiceProvider(); + public static LspContainedDocumentServiceProvider Instance = new(); bool IDocumentOperationService.CanApplyChange => true; @@ -39,7 +39,7 @@ private sealed class VirtualDocumentPropertiesService : DocumentPropertiesServic private VirtualDocumentPropertiesService() { } - public static VirtualDocumentPropertiesService Instance = new VirtualDocumentPropertiesService(); + public static VirtualDocumentPropertiesService Instance = new(); public override string? DiagnosticsLspClientName => _lspClientName; } diff --git a/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs b/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs index da8e523a027..99af5047e53 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs @@ -74,7 +74,7 @@ public ExtractInterfaceOptionsResult GetExtractInterfaceOptions( if (result.HasValue && result.Value) { - var includedMembers = viewModel.MemberContainers.Where(c => c.IsChecked).Select(c => c.Symbol); + var includedMembers = viewModel.MemberContainers.SelectAsArray(c => c.IsChecked, c => c.Symbol); return new ExtractInterfaceOptionsResult( isCancelled: false, diff --git a/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs b/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs index 2d9f8061648..46e7c2d09c8 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialogViewModel.cs @@ -689,7 +689,7 @@ internal GenerateTypeDialogViewModel( { // Populate the project list // Add the current project - new ProjectSelectItem(document.Project) + new(document.Project) }; // Add the rest of the projects diff --git a/src/roslyn/src/VisualStudio/Core/Def/Guids.cs b/src/roslyn/src/VisualStudio/Core/Def/Guids.cs index 11681a24fbd..e1f0cda08ed 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Guids.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Guids.cs @@ -49,7 +49,6 @@ internal static class Guids // option page guids from csharp\rad\pkg\guids.h public const string CSharpOptionPageAdvancedIdString = "8FD0B177-B244-4A97-8E37-6FB7B27DE3AF"; public const string CSharpOptionPageNamingStyleIdString = "294FBC9C-EF70-4AA0-BD4F-EB0C6A5908D7"; - public const string CSharpOptionPageIntelliSenseIdString = "EDE66829-7A36-4c5d-8E20-9290195DCF80"; public const string CSharpOptionPageCodeStyleIdString = "EAE577A7-ACB9-40F5-A7B1-D2878C3C7D6F"; public const string CSharpOptionPageFormattingGeneralIdString = "DA0446DD-55BA-401F-A364-7D3238412AE4"; public const string CSharpOptionPageFormattingIndentationIdString = "5E21D017-6D2A-4114-A1F1-C923F001CBBB"; @@ -94,7 +93,6 @@ internal static class Guids // option page guid from setupauthoring\vb\components\vblanguageservice.pkgdef public const string VisualBasicOptionPageVBSpecificIdString = "F1E1021E-A781-4862-9F4B-88746A288A67"; public const string VisualBasicOptionPageNamingStyleIdString = "BCA454E0-95E4-4877-B4CB-B1D642B7BAFA"; - public const string VisualBasicOptionPageIntelliSenseIdString = "04460A3B-1B5F-4402-BC6D-89A4F6F0A8D7"; public const string FSharpPackageIdString = "871D2A70-12A2-4e42-9440-425DD92A4116"; diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs index eb57d578616..6da3f32f5af 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsContainedLanguageFactory.cs @@ -61,7 +61,7 @@ private ProjectSystemProject FindMatchingProject(IVsHierarchy hierarchy, uint it return null; } - return this.Workspace.GetProjectWithHierarchyAndName(hierarchy, projectName); + return this.Workspace.Value.GetProjectWithHierarchyAndName(hierarchy, projectName); } public int GetLanguage(IVsHierarchy hierarchy, uint itemid, IVsTextBufferCoordinator bufferCoordinator, out IVsContainedLanguage language) diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs index 81f5773e507..90c4b7e7c7d 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsImmediateStatementCompletion2.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.DebuggerIntelliSense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; @@ -50,8 +51,10 @@ int IVsImmediateStatementCompletion2.InstallStatementCompletion(int install, IVs { if (!this.filters.TryGetValue(textView, out var filter)) { + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + filter = new DebuggerIntelliSenseFilter( - this.EditorAdaptersFactoryService.GetWpfTextView(textView), + editorAdaptersFactoryService.GetWpfTextView(textView), this.Package.ComponentModel, this.Package.ComponentModel.GetService()); this.filters[textView] = filter; @@ -83,13 +86,14 @@ int IVsImmediateStatementCompletion2.SetCompletionContext(string filePath, // commit, so we need to drag the IVsTextLines around, too. Marshal.ThrowExceptionForHR(textView.GetBuffer(out var debuggerBuffer)); - var view = EditorAdaptersFactoryService.GetWpfTextView(textView); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var view = editorAdaptersFactoryService.GetWpfTextView(textView); // Sometimes, they give us a null context buffer. In that case, there's probably not any // work to do. if (buffer != null) { - var contextBuffer = EditorAdaptersFactoryService.GetDataBuffer(buffer); + var contextBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); if (!contextBuffer.ContentType.IsOfType(this.ContentTypeName)) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs index 3fea5f9bf7e..72056f1f482 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageBlock.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; @@ -29,7 +30,9 @@ public int GetCurrentBlock( out string pbstrDescription, out int pfBlockAvailable) { - var snapshot = this.EditorAdaptersFactoryService.GetDataBuffer(pTextLines).CurrentSnapshot; + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + + var snapshot = editorAdaptersFactoryService.GetDataBuffer(pTextLines).CurrentSnapshot; var position = snapshot?.TryGetPosition(iCurrentLine, iCurrentChar); if (position == null) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs index e2da72836fe..490d0fd8b78 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageContextProvider.cs @@ -5,6 +5,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.F1Help; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TextManager.Interop; @@ -17,7 +18,8 @@ public int UpdateLanguageContext(uint dwHint, IVsTextLines pBuffer, Microsoft.Vi { return this.ThreadingContext.JoinableTaskFactory.Run(async () => { - var textBuffer = EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); var context = (IVsUserContext)pUC; if (textBuffer == null || context == null) diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs index 8cd5493da86..ccfa3c6b42e 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo.cs @@ -20,7 +20,7 @@ int IVsLanguageDebugInfo.GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCo { try { - return _languageDebugInfo.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID); + return _languageDebugInfo.Value.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -32,7 +32,7 @@ int IVsLanguageDebugInfo.GetLocationOfName(string pszName, out string pbstrMkDoc { try { - return _languageDebugInfo.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation); + return _languageDebugInfo.Value.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -44,7 +44,7 @@ int IVsLanguageDebugInfo.GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int { try { - return _languageDebugInfo.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset); + return _languageDebugInfo.Value.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -56,7 +56,7 @@ int IVsLanguageDebugInfo.GetProximityExpressions(IVsTextBuffer pBuffer, int iLin { try { - return _languageDebugInfo.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum); + return _languageDebugInfo.Value.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -68,7 +68,7 @@ int IVsLanguageDebugInfo.IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int { try { - return _languageDebugInfo.IsMappedLocation(pBuffer, iLine, iCol); + return _languageDebugInfo.Value.IsMappedLocation(pBuffer, iLine, iCol); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -80,7 +80,7 @@ int IVsLanguageDebugInfo.ResolveName(string pszName, uint dwFlags, out IVsEnumDe { try { - return _languageDebugInfo.ResolveName(pszName, dwFlags, out ppNames); + return _languageDebugInfo.Value.ResolveName(pszName, dwFlags, out ppNames); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { @@ -92,7 +92,7 @@ int IVsLanguageDebugInfo.ValidateBreakpointLocation(IVsTextBuffer pBuffer, int i { try { - return _languageDebugInfo.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan); + return _languageDebugInfo.Value.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index 4f3ce55b5f0..142575f7d46 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -41,7 +42,8 @@ public int Format(IVsTextLayer textLayer, TextSpan[] selections) private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, CancellationToken cancellationToken) { - var textBuffer = this.EditorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)textLayer); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer((IVsTextBuffer)textLayer); if (textBuffer == null) { return VSConstants.E_UNEXPECTED; @@ -56,7 +58,8 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella var documentSyntax = ParsedDocument.CreateSynchronously(document, cancellationToken); var text = documentSyntax.Text; var root = documentSyntax.Root; - var formattingOptions = textBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: true); + var editorOptionsService = this.Package.ComponentModel.GetService(); + var formattingOptions = textBuffer.GetSyntaxFormattingOptions(editorOptionsService, document.Project.GetFallbackAnalyzerOptions(), document.Project.Services, explicitFormat: true); var ts = selections.Single(); var start = text.Lines[ts.iStartLine].Start + ts.iStartIndex; @@ -65,7 +68,7 @@ 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 ruleFactory = Workspace.Value.Services.GetService(); // use formatting that return text changes rather than tree rewrite which is more expensive var formatter = document.GetRequiredLanguageService(); diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs index 6bf609cd254..0029116d63c 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs @@ -77,7 +77,8 @@ private void AddOrRemoveDropdown() return; } - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(buffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); var document = textBuffer?.AsTextContainer()?.GetRelatedDocuments().FirstOrDefault(); // TODO - Remove the TS check once they move the liveshare navbar to LSP. Then we can also switch to LSP // for the local navbar implementation. @@ -155,8 +156,9 @@ private void AddDropdownBar(IVsDropdownBarManager dropdownManager) return; } - var navigationBarClient = new NavigationBarClient(dropdownManager, _codeWindow, _languageService.SystemServiceProvider, _languageService.Workspace); - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(buffer); + var navigationBarClient = new NavigationBarClient(dropdownManager, _codeWindow, _languageService.SystemServiceProvider, _languageService.Workspace.Value); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer); var controllerFactoryService = _languageService.Package.ComponentModel.GetService(); var newController = controllerFactoryService.CreateController(navigationBarClient, textBuffer!); var hr = dropdownManager.AddDropdownBar(cCombos: 3, pClient: navigationBarClient); diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs index 1e3e8b50c8b..8bf2f54f553 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.Utilities; @@ -92,7 +93,9 @@ public int GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, out str showProgress: false); var cancellationToken = waitContext.UserCancellationToken; - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer == null) return default; @@ -135,7 +138,9 @@ public int GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, i if (_proximityExpressionsService == null) return null; - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer == null) return null; @@ -195,7 +200,7 @@ public int ResolveName(string? pszName, uint dwFlags, out IVsEnumDebugName? ppNa var cancellationToken = waitContext.UserCancellationToken; if (dwFlags == (uint)RESOLVENAMEFLAGS.RNF_BREAKPOINT) { - var solution = _languageService.Workspace.CurrentSolution; + var solution = _languageService.Workspace.Value.CurrentSolution; if (_breakpointService != null) { @@ -220,7 +225,7 @@ IVsDebugName CreateDebugName( // and using the blocked thread whenever possible. var document = breakpoint.Document; - var filePath = _languageService.Workspace.GetFilePath(document.Id); + var filePath = _languageService.Workspace.Value.GetFilePath(document.Id); // We're (unfortunately) blocking the UI thread here. So avoid async io as we actually // awant the IO to complete as quickly as possible, on this thread if necessary. @@ -264,7 +269,8 @@ private async Task ValidateBreakpointLocationAsync( if (_breakpointService == null) return VSConstants.E_FAIL; - var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(pBuffer); + var editorAdaptersFactoryService = _languageService.Package.ComponentModel.GetService(); + var textBuffer = editorAdaptersFactoryService.GetDataBuffer(pBuffer); if (textBuffer != null) { var snapshot = textBuffer.CurrentSnapshot; diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs index 6044e5dc322..f145d4962f0 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.cs @@ -36,39 +36,9 @@ internal abstract partial class AbstractLanguageService - /// Whether or not we have been set up. This is set once everything is wired up and cleared once tear down has begun. - /// - /// - /// We don't set this until we've completed setup. If something goes sideways during it, we will never register - /// with the shell and thus have a floating thing around that can't be safely shut down either. We're in a bad - /// state but trying to proceed will only make things worse. - /// - private bool _isSetUp; + private readonly Lazy _languageDebugInfo; + + internal readonly Lazy Workspace; protected abstract string ContentTypeName { get; } protected abstract string LanguageName { get; } @@ -81,10 +51,9 @@ protected AbstractLanguageService(TPackage package) Debug.Assert(!this.Package.JoinableTaskFactory.Context.IsOnMainThread, "Language service should be instantiated on background thread"); - this.EditorOptionsService = this.Package.ComponentModel.GetService(); - this.Workspace = this.Package.ComponentModel.GetService(); - this.EditorAdaptersFactoryService = this.Package.ComponentModel.GetService(); - this._languageDebugInfo = CreateLanguageDebugInfo(); + this.Workspace = this.Package.ComponentModel.DefaultExportProvider.GetExport(); + + this._languageDebugInfo = new Lazy(CreateLanguageDebugInfo); } private IThreadingContext ThreadingContext => this.Package.ComponentModel.GetService(); @@ -92,61 +61,26 @@ protected AbstractLanguageService(TPackage package) public override IServiceProvider SystemServiceProvider => Package; - /// - /// Setup and TearDown go in reverse order. - /// - public async Task SetupAsync(CancellationToken cancellationToken) + public void Setup(CancellationToken cancellationToken) { - // First, acquire any services we need throughout our lifetime. - // This method should only contain calls to acquire services off of the component model - // or service providers. Anything else which is more complicated should go in Initialize - // instead. - // Start off a background task to prime some components we'll need for editing. Task.Run(() => { - var formatter = this.Workspace.Services.GetLanguageServices(RoslynLanguageName).GetService(); + var formatter = this.Workspace.Value.Services.GetLanguageServices(RoslynLanguageName).GetService(); formatter?.GetDefaultFormattingRules(); }, cancellationToken).Forget(); - - await this.Package.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - // Creating the com aggregate has to happen on the UI thread. - this.ComAggregate = Interop.ComAggregate.CreateAggregatedObject(this); - - _isSetUp = true; - } - - internal void TearDown() - { - if (!_isSetUp) - { - throw new InvalidOperationException(); - } - - _isSetUp = false; - GC.SuppressFinalize(this); - } - - ~AbstractLanguageService() - { - if (!Environment.HasShutdownStarted && _isSetUp) - { - throw new InvalidOperationException("TearDown not called!"); - } } protected virtual void SetupNewTextView(IVsTextView textView) { Contract.ThrowIfNull(textView); - var wpfTextView = EditorAdaptersFactoryService.GetWpfTextView(textView); + var editorAdaptersFactoryService = this.Package.ComponentModel.GetService(); + var wpfTextView = editorAdaptersFactoryService.GetWpfTextView(textView); Contract.ThrowIfNull(wpfTextView, "Could not get IWpfTextView for IVsTextView"); Debug.Assert(!wpfTextView.Properties.ContainsProperty(typeof(AbstractVsTextViewFilter))); - var workspace = Package.ComponentModel.GetService(); - // The lifetime of CommandFilter is married to the view wpfTextView.GetOrCreateAutoClosingProperty(v => new StandaloneCommandFilter( @@ -219,7 +153,7 @@ private void ConditionallyCollapseOutliningRegions(IVsTextView textView, IWpfTex private VsLanguageDebugInfo CreateLanguageDebugInfo() { - var languageServices = this.Workspace.Services.GetLanguageServices(RoslynLanguageName); + var languageServices = this.Workspace.Value.Services.GetLanguageServices(RoslynLanguageName); return new VsLanguageDebugInfo( this.DebuggerLanguageId, @@ -235,7 +169,7 @@ protected virtual IVsContainedLanguage CreateContainedLanguage( return new ContainedLanguage( bufferCoordinator, this.Package.ComponentModel, - this.Workspace, + this.Workspace.Value, project.Id, project, this.LanguageServiceId); diff --git a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs index 4cfe2ae2473..cca33bb4458 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs @@ -24,12 +24,15 @@ internal abstract partial class AbstractPackage : Ab where TPackage : AbstractPackage where TLanguageService : AbstractLanguageService { - private TLanguageService? _languageService; - private PackageInstallerService? _packageInstallerService; private VisualStudioSymbolSearchService? _symbolSearchService; private IVsShell? _shell; + /// + /// Set to 1 if we've already preloaded project system components. Should be updated with + /// + private int _projectSystemComponentsPreloaded; + protected AbstractPackage() { } @@ -65,18 +68,33 @@ private async Task PackageInitializationMainThreadAsync(PackageLoadTasks package private Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks packageInitializationTasks, CancellationToken cancellationToken) { - RegisterLanguageService(typeof(TLanguageService), async cancellationToken => - { - // Ensure we're on the BG when creating the language service. - await TaskScheduler.Default; - - // Create the language service, tell it to set itself up, then store it in a field - // so we can notify it that it's time to clean up. - _languageService = CreateLanguageService(); - await _languageService.SetupAsync(cancellationToken).ConfigureAwait(false); - - return _languageService.ComAggregate!; - }); + AddService(typeof(TLanguageService), async (_, cancellationToken, _) => + { + // Ensure we're on the BG when creating the language service. + await TaskScheduler.Default; + + var languageService = CreateLanguageService(); + languageService.Setup(cancellationToken); + + // DevDiv 753309: + // We've redefined some VS interfaces that had incorrect PIAs. When + // we interop with native parts of VS, they always QI, so everything + // works. However, Razor is now managed, but assumes that the C# + // language service is native. When setting breakpoints, they + // get the language service from its GUID and cast it to IVsLanguageDebugInfo. + // This would QI the native lang service. Since we're managed and + // we've redefined IVsLanguageDebugInfo, the cast + // fails. To work around this, we put the LS inside a ComAggregate object, + // which always force a QueryInterface and allow their cast to succeed. + // + // This also fixes 752331, which is a similar problem with the + // exception assistant. + + // Creating the com aggregate has to happen on the UI thread. + await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + return Interop.ComAggregate.CreateAggregatedObject(languageService); + }, + promote: true); // Misc workspace has to be up and running by the time our package is usable so that it can track running // doc events and appropriately map files to/from it and other relevant workspaces (like the @@ -136,10 +154,6 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation protected abstract IEnumerable CreateEditorFactories(); protected abstract TLanguageService CreateLanguageService(); - // When registering a language service, we need to take its ComAggregate wrapper. - protected void RegisterLanguageService(Type t, Func> serviceCreator) - => AddService(t, async (container, cancellationToken, type) => await serviceCreator(cancellationToken).ConfigureAwait(true), promote: true); - protected override void Dispose(bool disposing) { if (disposing) @@ -150,13 +164,6 @@ protected override void Dispose(bool disposing) { UnregisterObjectBrowserLibraryManager(); } - - // If we've created the language service then tell it it's time to clean itself up now. - if (_languageService != null) - { - _languageService.TearDown(); - _languageService = null; - } } base.Dispose(disposing); @@ -175,4 +182,26 @@ protected virtual void UnregisterObjectBrowserLibraryManager() // it is virtual rather than abstract to not break other languages which derived from our // base package implementations } + + protected void PreloadProjectSystemComponents() + { + if (Interlocked.CompareExchange(ref _projectSystemComponentsPreloaded, value: 1, comparand: 0) == 1) + return; + + // Preload some components so later uses don't block. This is specifically to help out csproj and msvbprj project systems. They push changes + // to us on the UI thread as fundamental part of their design. This causes blocking on the UI thread as we create MEF components and JIT code + // for the first time, even though those components could have been loaded on the background thread first. This method is called from the two places + // we expose a service for the project systems to create us; this can be called by VS's preloading support on a background thread to ensure this is ran + // on a background thread so the later calls on the UI thread will block less. For CPS projects, we don't need this, as CPS already creates us on + // background threads, so we can just let things load as they're pulled in. + // + // The expectation is no thread switching should happen here; if we're being called on a background thread that means we're being pulled in + // by the preloading logic. If we're being called on the UI thread, that means we might be getting created directly by the project systems + // and the UI thread is already blocked, so a switch to the background thread won't unblock the UI thread and might delay us even further. + // + // As long as there's something that's not cheap to load later, and it'll definitely be used in all csproj/msvbprj scenarios, then it's worth + // putting here to preload. + var workspace = this.ComponentModel.GetService(); + workspace.PreloadProjectSystemComponents(this.RoslynLanguageName); + } } diff --git a/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index dfcbf133b8a..ba5495f5eb6 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -233,7 +233,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_using_directive_placement", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferredUsingDirectivePlacement")}, {"dotnet_provide_date_and_time_completions", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ProvideDateAndTimeCompletions")}, {"dotnet_log_telemetry_for_background_analyzer_execution", new FeatureFlagStorage(@"Roslyn.LogTelemetryForBackgroundAnalyzerExecution")}, - {"dotnet_lightbulb_skip_executing_deprioritized_analyzers", new FeatureFlagStorage(@"Roslyn.LightbulbSkipExecutingDeprioritizedAnalyzers")}, #pragma warning disable CS0612 // Type or member is obsolete {"dotnet_auto_xml_doc_comment_generation", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.Automatic XML Doc Comment Generation", "TextEditor.VisualBasic.Specific.AutoComment")}, #pragma warning restore diff --git a/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs b/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs index 9992c08ca69..fabda7ce6ed 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/PdbSourceDocument/PdbSourceDocumentOutputWindowLogger.cs @@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.LanguageServices.PdbSourceDocument; [Export(typeof(IPdbSourceDocumentLogger)), Shared] internal sealed class PdbSourceDocumentOutputWindowLogger : IPdbSourceDocumentLogger, IDisposable { - private static readonly Guid s_outputPaneGuid = new Guid("f543e896-2e9c-48b8-8fac-d1d5030b4b89"); + private static readonly Guid s_outputPaneGuid = new("f543e896-2e9c-48b8-8fac-d1d5030b4b89"); private IVsOutputWindowPane? _outputPane; private readonly IThreadingContext _threadingContext; diff --git a/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs b/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs index f4664d98806..ed9e8737d64 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/PickMembers/VisualStudioPickMembersService.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PickMembers; using Microsoft.VisualStudio.Language.Intellisense; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.PickMembers; @@ -38,7 +38,7 @@ public PickMembersResult PickMembers( if (result == true) { return new PickMembersResult( - [.. viewModel.MemberContainers.Where(c => c.IsChecked).Select(c => c.Symbol)], + viewModel.MemberContainers.SelectAsArray(c => c.IsChecked, c => c.Symbol), options, viewModel.SelectedAll); } diff --git a/src/roslyn/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml b/src/roslyn/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml index 7ebc899c77c..9005cbfe3d4 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml +++ b/src/roslyn/src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml @@ -7,7 +7,7 @@ xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0" xmlns:pp="clr-namespace:Microsoft.VisualStudio.LanguageServices.Implementation.PreviewPane" x:ClassModifier="internal" mc:Ignorable="d" - Focusable="True" IsTabStop="False" TextOptions.TextFormattingMode="Display" + Focusable="True" IsTabStop="False" TextOptions.TextFormattingMode="Ideal" Foreground="{DynamicResource {x:Static vsui:EnvironmentColors.PanelTextBrushKey}}" d:DesignHeight="300" d:DesignWidth="400" x:Name="ThisPane" diff --git a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs index 98232c926e5..a9a19c1665b 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject.cs @@ -53,12 +53,8 @@ internal abstract partial class AbstractLegacyProject private static readonly char[] PathSeparatorCharacters = [Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar]; - #region Mutable fields that should only be used from the UI thread - private readonly SolutionEventsBatchScopeCreator _batchScopeCreator; - #endregion - public AbstractLegacyProject( string projectSystemName, IVsHierarchy hierarchy, diff --git a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs index dc805488e09..0816942fef4 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs @@ -4,245 +4,226 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; +using Microsoft.Internal.VisualStudio.Shell.Interop; +using Microsoft.Internal.VisualStudio.Shell.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy; /// -/// Creates batch scopes for projects based on IVsSolutionEvents. This is useful for projects types that don't otherwise have +/// Creates batch scopes for projects based on solution and running document table events. This is useful for projects types that don't otherwise have /// good batching concepts. /// -/// All members of this class are affinitized to the UI thread. [Export(typeof(SolutionEventsBatchScopeCreator))] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class SolutionEventsBatchScopeCreator(IThreadingContext threadingContext, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider) +internal sealed class SolutionEventsBatchScopeCreator { + /// + /// A lock for mutating all objects in this object. This class isn't expected to have any "interesting" locking requirements, so this should just be acquired + /// in all methods. + /// + private readonly object _gate = new(); private readonly List<(ProjectSystemProject project, IVsHierarchy hierarchy, ProjectSystemProject.BatchScope batchScope)> _fullSolutionLoadScopes = []; - private readonly IThreadingContext _threadingContext = threadingContext; - private readonly IServiceProvider _serviceProvider = serviceProvider; - + /// + /// The cookie for our subscription to the running document table. Null if we're not currently subscribed. + /// private uint? _runningDocumentTableEventsCookie; private bool _isSubscribedToSolutionEvents = false; - private bool _solutionLoaded = false; - public void StartTrackingProject(ProjectSystemProject project, IVsHierarchy hierarchy) - { - _threadingContext.ThrowIfNotOnUIThread(); + private readonly IVsBackgroundSolution _backgroundSolution; + private readonly IVsRunningDocumentTable _runningDocumentTable; - EnsureSubscribedToSolutionEvents(); + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public SolutionEventsBatchScopeCreator([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider) + { + // Fetch services we're going to need later; these are all free-threaded and cacheable on creation, and since we're only going to be + // creating this part once we're in a solution load, the services would have already been created. + _backgroundSolution = (IVsBackgroundSolution)serviceProvider.GetService(typeof(SVsBackgroundSolution)); + _runningDocumentTable = (IVsRunningDocumentTable)serviceProvider.GetService(typeof(SVsRunningDocumentTable)); + } - if (!_solutionLoaded) + public void StartTrackingProject(ProjectSystemProject project, IVsHierarchy hierarchy) + { + lock (_gate) { - _fullSolutionLoadScopes.Add((project, hierarchy, project.CreateBatchScope())); + EnsureSubscribedToSolutionEvents(); + + if (_backgroundSolution.IsSolutionOpening) + { + _fullSolutionLoadScopes.Add((project, hierarchy, project.CreateBatchScope())); - EnsureSubscribedToRunningDocumentTableEvents(); + EnsureSubscribedToRunningDocumentTableEvents(); + } } } public void StopTrackingProject(ProjectSystemProject project) { - _threadingContext.ThrowIfNotOnUIThread(); - - foreach (var scope in _fullSolutionLoadScopes) + lock (_gate) { - if (scope.project == project) + foreach (var scope in _fullSolutionLoadScopes) { - scope.batchScope.Dispose(); - _fullSolutionLoadScopes.Remove(scope); - break; + if (scope.project == project) + { + scope.batchScope.Dispose(); + _fullSolutionLoadScopes.Remove(scope); + break; + } } - } - EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + } } - private void StopTrackingAllProjects() + /// + /// Closes all batch scopes for all currently tracked projects, called when the solution has finished loading. + /// + private Task StopTrackingAllProjectsAsync() { - _threadingContext.ThrowIfNotOnUIThread(); + ImmutableArray batchScopeTasks; - foreach (var (_, _, batchScope) in _fullSolutionLoadScopes) + lock (_gate) { - batchScope.Dispose(); - } + // Kick off on a background thread the work to close each of the batches. The expectation is each batch closure will fairly quickly hit the solution-level + // semaphore, so we don't need to explicitly throttle this work here. + batchScopeTasks = _fullSolutionLoadScopes.SelectAsArray(static s => Task.Run(() => s.batchScope.DisposeAsync().AsTask())); - _fullSolutionLoadScopes.Clear(); + // We always want to ensure we clear out the list and unsubscribe, even if cancellation has been requested. + _fullSolutionLoadScopes.Clear(); + + EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + } - EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + return Task.WhenAll(batchScopeTasks); } private void StopTrackingAllProjectsMatchingHierarchy(IVsHierarchy hierarchy) { - _threadingContext.ThrowIfNotOnUIThread(); - - for (var i = 0; i < _fullSolutionLoadScopes.Count; i++) + lock (_gate) { - if (_fullSolutionLoadScopes[i].hierarchy == hierarchy) + for (var i = 0; i < _fullSolutionLoadScopes.Count; i++) { - _fullSolutionLoadScopes[i].batchScope.Dispose(); - _fullSolutionLoadScopes.RemoveAt(i); - - // Go back by one so we re-check the same index - i--; + if (_fullSolutionLoadScopes[i].hierarchy == hierarchy) + { + _fullSolutionLoadScopes[i].batchScope.Dispose(); + _fullSolutionLoadScopes.RemoveAt(i); + + // Go back by one so we re-check the same index + i--; + } } - } - EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded(); + } } private void EnsureSubscribedToSolutionEvents() { - _threadingContext.ThrowIfNotOnUIThread(); - - if (_isSubscribedToSolutionEvents) + lock (_gate) { - return; - } + if (_isSubscribedToSolutionEvents) + { + return; + } - var solution = (IVsSolution)_serviceProvider.GetService(typeof(SVsSolution)); + // We never unsubscribe from these, so we just throw out the subscription. We could consider unsubscribing if/when all our + // projects are unloaded, but it seems fairly unnecessary -- it'd only be useful if somebody closed one solution but then + // opened other solutions in entirely different languages from there. + _ = _backgroundSolution.SubscribeListener(new SolutionEventsEventListener(this)); - // We never unsubscribe from these, so we just throw out the cookie. We could consider unsubscribing if/when all our - // projects are unloaded, but it seems fairly unnecessary -- it'd only be useful if somebody closed one solution but then - // opened other solutions in entirely different languages from there. - if (ErrorHandler.Succeeded(solution.AdviseSolutionEvents(new SolutionEventsEventSink(this), out _))) - { _isSubscribedToSolutionEvents = true; } - - // It's possible that we're loading after the solution has already fully loaded, so see if we missed the event - var shellMonitorSelection = (IVsMonitorSelection)_serviceProvider.GetService(typeof(SVsShellMonitorSelection)); - - if (ErrorHandler.Succeeded(shellMonitorSelection.GetCmdUIContextCookie(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_guid, out var fullyLoadedContextCookie))) - { - if (ErrorHandler.Succeeded(shellMonitorSelection.IsCmdUIContextActive(fullyLoadedContextCookie, out var fActive)) && fActive != 0) - { - _solutionLoaded = true; - } - } } private void EnsureSubscribedToRunningDocumentTableEvents() { - _threadingContext.ThrowIfNotOnUIThread(); - - if (_runningDocumentTableEventsCookie.HasValue) + lock (_gate) { - return; - } - - var runningDocumentTable = (IVsRunningDocumentTable)_serviceProvider.GetService(typeof(SVsRunningDocumentTable)); + if (_runningDocumentTableEventsCookie.HasValue) + { + return; + } - if (ErrorHandler.Succeeded(runningDocumentTable.AdviseRunningDocTableEvents(new RunningDocumentTableEventSink(this, runningDocumentTable), out var runningDocumentTableEventsCookie))) - { - _runningDocumentTableEventsCookie = runningDocumentTableEventsCookie; + if (ErrorHandler.Succeeded(_runningDocumentTable.AdviseRunningDocTableEvents(new RunningDocumentTableEventSink(this, _runningDocumentTable), out var runningDocumentTableEventsCookie))) + { + _runningDocumentTableEventsCookie = runningDocumentTableEventsCookie; + } } } private void EnsureUnsubscribedFromRunningDocumentTableEventsIfNoLongerNeeded() { - _threadingContext.ThrowIfNotOnUIThread(); - - if (!_runningDocumentTableEventsCookie.HasValue) + lock (_gate) { - return; - } + if (!_runningDocumentTableEventsCookie.HasValue) + { + return; + } - // If we don't have any scopes left, then there is no reason to be subscribed to Running Document Table events, because - // there won't be any scopes to complete. - if (_fullSolutionLoadScopes.Count > 0) - { - return; - } + // If we don't have any scopes left, then there is no reason to be subscribed to Running Document Table events, because + // there won't be any scopes to complete. + if (_fullSolutionLoadScopes.Count > 0) + { + return; + } - var runningDocumentTable = (IVsRunningDocumentTable)_serviceProvider.GetService(typeof(SVsRunningDocumentTable)); - runningDocumentTable.UnadviseRunningDocTableEvents(_runningDocumentTableEventsCookie.Value); - _runningDocumentTableEventsCookie = null; + _runningDocumentTable.UnadviseRunningDocTableEvents(_runningDocumentTableEventsCookie.Value); + _runningDocumentTableEventsCookie = null; + } } - private sealed class SolutionEventsEventSink : IVsSolutionEvents, IVsSolutionLoadEvents + private sealed class SolutionEventsEventListener : IVsAsyncSolutionEventListener { private readonly SolutionEventsBatchScopeCreator _scopeCreator; - public SolutionEventsEventSink(SolutionEventsBatchScopeCreator scopeCreator) + public SolutionEventsEventListener(SolutionEventsBatchScopeCreator scopeCreator) => _scopeCreator = scopeCreator; - int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename) + public async ValueTask OnAfterOpenSolutionAsync(AfterOpenSolutionArgs args, CancellationToken cancellationToken) { - Contract.ThrowIfTrue(_scopeCreator._fullSolutionLoadScopes.Any()); + // NOTE: the cancellationToken here might be cancelled if the user has requested that we cancel the solution load. If the cancellation happened + // prior to this method being invoked, we might see this method invoked with the token cancelled from the very start. We want to make sure + // we get rid of all the batch scopes in that case before checking the cancellation token. Thus we won't pass the token to StopTrackingAllProjectsAsync. + await _scopeCreator.StopTrackingAllProjectsAsync().WithCancellation(cancellationToken).ConfigureAwait(false); + } - _scopeCreator._solutionLoaded = false; + #region Unimplemented Members - return VSConstants.S_OK; + public void OnUnhandledException(Exception exception) + { } - int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved) + public ValueTask OnBeforeOpenSolutionAsync(BeforeOpenSolutionArgs args, CancellationToken cancellationToken) { - _scopeCreator._solutionLoaded = false; - - return VSConstants.S_OK; + return ValueTask.CompletedTask; } - int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution) + public ValueTask OnBeforeCloseSolutionAsync(BeforeCloseSolutionArgs args, CancellationToken cancellationToken) { - _scopeCreator._solutionLoaded = true; - _scopeCreator.StopTrackingAllProjects(); - - return VSConstants.S_OK; + return ValueTask.CompletedTask; } - #region Unimplemented Members - - int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete() - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) - => VSConstants.E_NOTIMPL; - - int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) - => VSConstants.E_NOTIMPL; - - int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins() - => VSConstants.E_NOTIMPL; - - int IVsSolutionLoadEvents.OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle) + public ValueTask OnAfterCloseSolutionAsync(AfterCloseSolutionArgs args, CancellationToken cancellationToken) { - pfShouldDelayLoadToNextIdle = false; - return VSConstants.E_NOTIMPL; + return ValueTask.CompletedTask; } - int IVsSolutionLoadEvents.OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) - => VSConstants.E_NOTIMPL; - - int IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) - => VSConstants.E_NOTIMPL; + public ValueTask OnAfterRenameSolutionAsync(AfterRenameSolutionArgs args, CancellationToken cancellationToken) + { + return ValueTask.CompletedTask; + } #endregion } diff --git a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index 2598fdbf8b4..71b86772101 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -1630,4 +1630,16 @@ private async Task UpdateUIContextAsync(string language, CancellationToken cance // value after the main thread switch. uiContext.IsActive = this.CurrentSolution.Projects.Any(p => p.Language == language); } + + internal void PreloadProjectSystemComponents(string languageName) + { + // Ensure we have any listeners for WellKnownEventListeners.Workspace warmed up, since these are otherwise created + // the first time we make a change to the CurrentSolution + base.EnsureEventListeners(); + + // Load up the command line parser and warm it up. This generally ensures we have our language specific binaries loaded + // and we have the command line parser ready to go, since those tend to be more expensive things to JIT. + var commandLineParserService = Services.GetRequiredLanguageService(languageName); + commandLineParserService.Parse([], null, isInteractive: false, sdkDirectory: null); + } } diff --git a/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs b/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs index 3afa348c505..5a83164d7a4 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClient.cs @@ -1191,7 +1191,7 @@ public void Clear() } internal TestAccessor GetTestAccessor() - => new TestAccessor(this); + => new(this); internal readonly struct TestAccessor(SnippetExpansionClient instance) { diff --git a/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 37d0865b8bb..f11d161783a 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -484,7 +484,7 @@ private async Task>> Ge var uniqueDiagnosticIds = group.SelectMany(kvp => kvp.Value.Select(d => d.Id)).ToImmutableHashSet(); var diagnosticService = _workspace.Services.GetRequiredService(); var latestProjectDiagnostics = (await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken) + project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)).Where(IsDocumentDiagnostic); latestDocumentDiagnosticsMap.Clear(); @@ -563,7 +563,7 @@ private async Task>> Get var uniqueDiagnosticIds = diagnostics.Select(d => d.Id).ToImmutableHashSet(); var diagnosticService = _workspace.Services.GetRequiredService(); var latestDiagnosticsFromDiagnosticService = (await diagnosticService.GetDiagnosticsForIdsAsync( - project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, shouldIncludeAnalyzer: null, includeLocalDocumentDiagnostics: true, cancellationToken) + project, documentIds: default, diagnosticIds: uniqueDiagnosticIds, AnalyzerFilter.All, includeLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)); latestDiagnosticsToFix.Clear(); diff --git a/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs b/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs index b146356ef20..00e906eeb3d 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs @@ -13,10 +13,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Telemetry; internal sealed class CodeMarkerLogger : ILogger { - public static readonly CodeMarkerLogger Instance = new CodeMarkerLogger(); + public static readonly CodeMarkerLogger Instance = new(); private static readonly Dictionary>> s_blockMap - = new Dictionary>>() + = new() { { FunctionId.NavigateTo_Search, new List>() { @@ -111,7 +111,7 @@ private static readonly Dictionary> s_map - = new Dictionary>() + = new() { { FunctionId.Rename_InlineSession, new List() { CodeMarkerEvent.perfVBRenameSymbolEnd } }, { FunctionId.BackgroundCompiler_BuildCompilationsAsync, new List() { CodeMarkerEvent.perfVBCompilerReachedBoundState, CodeMarkerEvent.perfVBCompilerReachedCompiledState } }, diff --git a/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs b/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs index 4c760947edc..8a4046c1cdf 100644 --- a/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs +++ b/src/roslyn/src/VisualStudio/Core/Def/Utilities/ClipboardHelpers.cs @@ -92,7 +92,7 @@ internal static class ClipboardHelpers } private static FORMATETC CreateFormatEtc(ushort format) - => new FORMATETC + => new() { cfFormat = format, ptd = IntPtr.Zero, diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs index a9131b1c901..3407e12c36f 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs @@ -38,8 +38,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; internal abstract partial class AbstractCodeModelService : ICodeModelService { - private readonly ConditionalWeakTable> _treeToNodeKeyMaps = - new ConditionalWeakTable>(); + private readonly ConditionalWeakTable> _treeToNodeKeyMaps = new(); protected readonly ISyntaxFactsService SyntaxFactsService; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs index 32a59be00e2..92d28022732 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; internal static class CodeModelExtensions { private static readonly SymbolDisplayFormat s_fullNameWithEscapedKeywordsFormat = - new SymbolDisplayFormat( + new( globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, propertyStyle: SymbolDisplayPropertyStyle.NameOnly, diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs index c4c4058e505..3b620d941a6 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/CodeModelProjectCache.cs @@ -23,8 +23,8 @@ internal sealed partial class CodeModelProjectCache private readonly ProjectId _projectId; private readonly ICodeModelInstanceFactory _codeModelInstanceFactory; - private readonly Dictionary _cache = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly object _cacheGate = new object(); + private readonly Dictionary _cache = new(StringComparer.OrdinalIgnoreCase); + private readonly object _cacheGate = new(); private EnvDTE.CodeModel? _rootCodeModel; private bool _zombied; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs index 7988429afd6..43cdf3c4641 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/InternalElements/CodeClass.cs @@ -17,7 +17,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Inter public sealed class CodeClass : AbstractCodeType, EnvDTE.CodeClass, EnvDTE80.CodeClass2, ICodeClassBase { private static readonly SymbolDisplayFormat s_BaseNameFormat = - new SymbolDisplayFormat( + new( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, memberOptions: SymbolDisplayMemberOptions.IncludeContainingType, diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs index 9231bdff645..0196136da31 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.AttributeInfo.cs @@ -10,7 +10,7 @@ internal abstract partial class AbstractMethodXmlBuilder { private readonly struct AttributeInfo { - public static readonly AttributeInfo Empty = new AttributeInfo(); + public static readonly AttributeInfo Empty = new(); public readonly string Name; public readonly string Value; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs index 7ded0b68a93..d005c9f19f8 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/MethodXml/AbstractMethodXmlBuilder.cs @@ -192,7 +192,7 @@ private static AttributeInfo ImplicitAttribute(bool? @implicit) } private static AttributeInfo LineNumberAttribute(int lineNumber) - => new AttributeInfo(LineAttributeName, lineNumber.ToString()); + => new(LineAttributeName, lineNumber.ToString()); private static AttributeInfo NameAttribute(string name) { @@ -205,7 +205,7 @@ private static AttributeInfo NameAttribute(string name) } private static AttributeInfo RankAttribute(int rank) - => new AttributeInfo(RankAttributeName, rank.ToString()); + => new(RankAttributeName, rank.ToString()); private static AttributeInfo SpecialCastKindAttribute(SpecialCastKind? specialCastKind = null) => specialCastKind switch diff --git a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs index 56301cb6fa7..a460160331f 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModel.cs @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; /// internal sealed class ProjectCodeModel : IProjectCodeModel { - private readonly NonReentrantLock _guard = new NonReentrantLock(); + private readonly NonReentrantLock _guard = new(); private readonly IThreadingContext _threadingContext; private readonly ProjectId _projectId; private readonly ICodeModelInstanceFactory _codeModelInstanceFactory; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs index 362002eb2ba..6bc33157b58 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/AbstractOptionPreviewViewModel.cs @@ -234,8 +234,8 @@ protected void AddUnusedParameterOption(OptionStore optionStore, string title, s { var unusedParameterPreferences = new List { - new CodeStylePreference(ServicesVSResources.Non_public_methods, isChecked: false), - new CodeStylePreference(ServicesVSResources.All_methods, isChecked: true), + new(ServicesVSResources.Non_public_methods, isChecked: false), + new(ServicesVSResources.All_methods, isChecked: true), }; var enumValues = new[] diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs index 6e62f172c51..298c24354fe 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/CodeStyleNoticeTextBlock.xaml.cs @@ -18,7 +18,7 @@ internal partial class CodeStyleNoticeTextBlock : TextBlock public CodeStyleNoticeTextBlock() => InitializeComponent(); - public static readonly Uri CodeStylePageHeaderLearnMoreUri = new Uri(UseEditorConfigUrl); + public static readonly Uri CodeStylePageHeaderLearnMoreUri = new(UseEditorConfigUrl); public static string CodeStylePageHeader => ServicesVSResources.Code_style_header_use_editor_config; public static string CodeStylePageHeaderLearnMoreText => ServicesVSResources.Learn_more; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs index 6538e6ae7f8..72647c3a456 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/GridOptionPreviewControl.xaml.cs @@ -29,7 +29,7 @@ internal partial class GridOptionPreviewControl : AbstractOptionPageControl private readonly IEnumerable<(string feature, ImmutableArray options)> _groupedEditorConfigOptions; private readonly string _language; - public static readonly Uri CodeStylePageHeaderLearnMoreUri = new Uri(UseEditorConfigUrl); + public static readonly Uri CodeStylePageHeaderLearnMoreUri = new(UseEditorConfigUrl); public static string CodeStylePageHeader => ServicesVSResources.Code_style_header_use_editor_config; public static string CodeStylePageHeaderLearnMoreText => ServicesVSResources.Learn_more; public static string DescriptionHeader => ServicesVSResources.Description; diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs index 8d0bd78469f..a53e99877a0 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.LanguageServices.Implementation.Options.Style.NamingPreferences; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; +using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Style; @@ -97,9 +98,9 @@ internal void UpdateSpecificationList(ManageSymbolSpecificationsDialogViewModel var symbolSpecifications = viewModel.Items.Cast().Select(n => new SymbolSpecification( n.ID, n.ItemName, - [.. n.SymbolKindList.Where(s => s.IsChecked).Select(k => k.CreateSymbolOrTypeOrMethodKind())], - [.. n.AccessibilityList.Where(s => s.IsChecked).Select(a => a._accessibility)], - [.. n.ModifierList.Where(s => s.IsChecked).Select(m => new SymbolSpecification.ModifierKind(m._modifier.Modifiers))])); + n.SymbolKindList.SelectAsArray(s => s.IsChecked, k => k.CreateSymbolOrTypeOrMethodKind()), + n.AccessibilityList.SelectAsArray(s => s.IsChecked, a => a._accessibility), + n.ModifierList.SelectAsArray(s => s.IsChecked, m => new SymbolSpecification.ModifierKind(m._modifier.Modifiers)))); Specifications.Clear(); foreach (var specification in symbolSpecifications) diff --git a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs index 2d003039619..2ae34d4b071 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs @@ -144,9 +144,9 @@ internal SymbolSpecification GetSymbolSpecification() return new SymbolSpecification( ID, ItemName, - [.. SymbolKindList.Where(s => s.IsChecked).Select(s => s.CreateSymbolOrTypeOrMethodKind())], - [.. AccessibilityList.Where(a => a.IsChecked).Select(a => a._accessibility)], - [.. ModifierList.Where(m => m.IsChecked).Select(m => new ModifierKind(m._modifier.Modifiers))]); + SymbolKindList.SelectAsArray(s => s.IsChecked, s => s.CreateSymbolOrTypeOrMethodKind()), + AccessibilityList.SelectAsArray(a => a.IsChecked, a => a._accessibility), + ModifierList.SelectAsArray(m => m.IsChecked, m => new ModifierKind(m._modifier.Modifiers))); } internal bool TrySubmit() diff --git a/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemCollectionSource.cs b/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemCollectionSource.cs index 18ee6299576..bfc1cddcaad 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemCollectionSource.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemCollectionSource.cs @@ -32,10 +32,11 @@ private sealed class RootSymbolTreeItemCollectionSource( rootProvider, hierarchyItem, hasItemsDefault: GetHasItemsDefaultValue(hierarchyItem)); /// - /// Whether or not this root solution explorer node has been expanded or not. Until it is first expanded, - /// we do no work so as to avoid CPU time and rooting things like syntax nodes. + /// Whether or not this root item has ever been asked to have its + /// property computed. This happens when an item is first explicitly expanded by a user, or if the user opens + /// the file in the editor (as we eagerly pre-compute the item in that case). /// - private volatile int _hasEverBeenExpanded; + private volatile int _hasEverBeenAskedToCompute; private static bool? GetHasItemsDefaultValue(IVsHierarchyItem hierarchyItem) // If this is not a c#/vb file initially, then mark this file as having no symbolic children. @@ -55,12 +56,12 @@ public void Reset() // it will never leave that state from that point on, and we'll be stuck in an invalid state. } - public async Task UpdateIfEverExpandedAsync(CancellationToken cancellationToken) + public async Task UpdateIfEverBeenAskedToComputeAsync(CancellationToken cancellationToken) { - // If we haven't been initialized yet, then we don't have to do anything. We will get called again - // in the future as documents are mutated, and we'll ignore until the point that the user has at - // least expanded this node once. - if (_hasEverBeenExpanded == 0) + // If we haven't ever been asked to compute items, then we don't have to do anything. We will get called + // again in the future as documents are mutated, and we'll ignore until the point that the user has at least + // expanded this node once. + if (_hasEverBeenAskedToCompute == 0) return; // Try to find a roslyn document for this file path. Note: it is intentional that we continue onwards, @@ -132,17 +133,18 @@ public async Task UpdateIfEverExpandedAsync(CancellationToken cancellationToken) bool IAttachedCollectionSource.HasItems => _childCollection.HasItems; + public void EnsureItemsComputed() + { + // If this was the first time this node was asked to compute, then kick off the initial work to do so. + if (Interlocked.CompareExchange(ref _hasEverBeenAskedToCompute, 1, 0) == 0) + _rootProvider._updateSourcesQueue.AddWork(_hierarchyItem.CanonicalName); + } + IEnumerable IAttachedCollectionSource.Items { get { - if (Interlocked.CompareExchange(ref _hasEverBeenExpanded, 1, 0) == 0) - { - // This was the first time this node was expanded. Kick off the initial work to - // compute the items for it. - _rootProvider._updateSourcesQueue.AddWork(_hierarchyItem.CanonicalName); - } - + EnsureItemsComputed(); return _childCollection.Items; } } diff --git a/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemSourceProvider.cs b/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemSourceProvider.cs index ba1f679c8b5..8a43903283e 100644 --- a/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemSourceProvider.cs +++ b/src/roslyn/src/VisualStudio/Core/Impl/SolutionExplorer/SymbolTree/RootSymbolTreeItemSourceProvider.cs @@ -18,7 +18,6 @@ using Microsoft.CodeAnalysis.GoToImplementation; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Threading; using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; @@ -92,12 +91,18 @@ public RootSymbolTreeItemSourceProvider( this.Listener, this.ThreadingContext.DisposalToken); + // Register for workspace changes so that if any documents change, we can update the symbol tree *as long as it + // has been expanded at least once* to reflect the new state of the document. this._workspace.RegisterWorkspaceChangedHandler( e => { var oldPath = e.OldSolution.GetDocument(e.DocumentId)?.FilePath; var newPath = e.NewSolution.GetDocument(e.DocumentId)?.FilePath; + // Update both the old and new paths. That way if the path changed, we'll remove the old source and add + // in the new one. Note: if the paths are the same, this will just add one entry to the queue as we + // dedupe based on file path. + if (oldPath != null) _updateSourcesQueue.AddWork(oldPath); @@ -106,6 +111,34 @@ public RootSymbolTreeItemSourceProvider( }, options: new WorkspaceEventOptions(RequiresMainThread: false)); + // Register for document open events so that if a document is opened, we can preemptively update the symbol tree + // even if it was never opened before. We do this as users expect the root symbol tree arrow to be there or not + // if the file has no symbols within it. We don't do this for closed documents as it would be extremely + // expensive to go to every file and read/parse it. However, once opened, we're going to do all that work + // anyways so it is worthwhile to force it to happen. + this._workspace.RegisterDocumentOpenedHandler( + e => + { + var filePath = e.Document.FilePath; + + if (filePath == null) + return; + + lock (_filePathToCollectionSources) + { + if (_filePathToCollectionSources.TryGetValue(filePath, out var pathSources)) + { + // For each source, go and ensure that the .Items collection is computed. If the source was + // already expanded, this will no-op. If it was never expanded, this will mark it as being + // expanded, and kick off the work back to us (in _updateSourcesQueue) to compute the actual + // items. + foreach (var source in pathSources) + source.EnsureItemsComputed(); + } + } + }, + new WorkspaceEventOptions(RequiresMainThread: false)); + this.ContextMenuController = new SymbolItemContextMenuController(this); } @@ -129,7 +162,7 @@ await Parallel.ForEachAsync( cancellationToken, async (source, cancellationToken) => { - await source.UpdateIfEverExpandedAsync(cancellationToken) + await source.UpdateIfEverBeenAskedToComputeAsync(cancellationToken) .ReportNonFatalErrorUnlessCancelledAsync(cancellationToken) .ConfigureAwait(false); }).ConfigureAwait(false); diff --git a/src/roslyn/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/roslyn/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index aba4e683dea..40a790172af 100644 --- a/src/roslyn/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/roslyn/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -157,14 +157,13 @@ void Method() var diagnosticAnalyzerService = (DiagnosticAnalyzerService)workspace.Services.GetRequiredService(); - var compilationWithAnalyzers = new CompilationWithAnalyzersPair( - projectCompilationWithAnalyzers: null, + var compilationWithAnalyzers = (await project.GetCompilationAsync()).WithAnalyzers( [.. analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType)], - project.AnalyzerOptions)); + project.AnalyzerOptions); var result = await diagnosticAnalyzerService.GetTestAccessor().AnalyzeProjectInProcessAsync(project, compilationWithAnalyzers, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); - var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.HostAnalyzers[0]]; + var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.Analyzers[0]]; // check result var diagnostics = analyzerResult.GetDocumentDiagnostics(project.DocumentIds.First(), AnalysisKind.Semantic); @@ -198,14 +197,13 @@ void Method() var diagnosticAnalyzerService = (DiagnosticAnalyzerService)workspace.Services.GetRequiredService(); var analyzers = analyzerReference.GetAnalyzers(project.Language).WhereAsArray(a => a.GetType() == analyzerType); - var compilationWithAnalyzers = new CompilationWithAnalyzersPair( - (await project.GetCompilationAsync()).WithAnalyzers(analyzers, project.AnalyzerOptions), - hostCompilationWithAnalyzers: null); + var compilationWithAnalyzers = + (await project.GetCompilationAsync()).WithAnalyzers(analyzers, project.AnalyzerOptions); var result = await diagnosticAnalyzerService.GetTestAccessor().AnalyzeProjectInProcessAsync(project, compilationWithAnalyzers, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); - var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.ProjectAnalyzers[0]]; + var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.Analyzers[0]]; // check result var diagnostics = analyzerResult.GetDocumentDiagnostics(project.DocumentIds.First(), AnalysisKind.Syntax); @@ -232,14 +230,11 @@ private static async Task AnalyzeAsync(TestWorkspace w var compilationWithAnalyzers = (await project.GetCompilationAsync()).WithAnalyzers( [.. analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType)], project.AnalyzerOptions); - var analyzerDriver = isHostAnalyzer - ? new CompilationWithAnalyzersPair(projectCompilationWithAnalyzers: null, compilationWithAnalyzers) - : new CompilationWithAnalyzersPair(compilationWithAnalyzers, hostCompilationWithAnalyzers: null); var result = await diagnosticAnalyzerService.GetTestAccessor().AnalyzeProjectInProcessAsync( - project, analyzerDriver, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken); + project, compilationWithAnalyzers, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken); - return result.AnalysisResult[(isHostAnalyzer ? analyzerDriver.HostAnalyzers : analyzerDriver.ProjectAnalyzers)[0]]; + return result.AnalysisResult[compilationWithAnalyzers.Analyzers[0]]; } private static TestWorkspace CreateWorkspace(string language, string code, ParseOptions options = null) diff --git a/src/roslyn/src/VisualStudio/Core/Test.Next/UnifiedSettings/UnifiedSettingsTests.cs b/src/roslyn/src/VisualStudio/Core/Test.Next/UnifiedSettings/UnifiedSettingsTests.cs index ec3f0f381f0..5b116e6ba95 100644 --- a/src/roslyn/src/VisualStudio/Core/Test.Next/UnifiedSettings/UnifiedSettingsTests.cs +++ b/src/roslyn/src/VisualStudio/Core/Test.Next/UnifiedSettings/UnifiedSettingsTests.cs @@ -31,18 +31,18 @@ public sealed class UnifiedSettingsTests /// Dictionary containing the option to unified setting path for C#. /// private static readonly ImmutableDictionary s_csharpUnifiedSettingsStorage = ImmutableDictionary.Empty. - Add(CompletionOptionsStorage.TriggerOnTypingLetters, "textEditor.csharp.intellisense.triggerCompletionOnTypingLetters"). - Add(CompletionOptionsStorage.TriggerOnDeletion, "textEditor.csharp.intellisense.triggerCompletionOnDeletion"). - Add(CompletionOptionsStorage.TriggerInArgumentLists, "textEditor.csharp.intellisense.triggerCompletionInArgumentLists"). - Add(CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems, "textEditor.csharp.intellisense.highlightMatchingPortionsOfCompletionListItems"). - Add(CompletionViewOptionsStorage.ShowCompletionItemFilters, "textEditor.csharp.intellisense.showCompletionItemFilters"). - Add(CompleteStatementOptionsStorage.AutomaticallyCompleteStatementOnSemicolon, "textEditor.csharp.intellisense.completeStatementOnSemicolon"). - Add(CompletionOptionsStorage.SnippetsBehavior, "textEditor.csharp.intellisense.snippetsBehavior"). - Add(CompletionOptionsStorage.EnterKeyBehavior, "textEditor.csharp.intellisense.returnKeyCompletionBehavior"). - Add(CompletionOptionsStorage.ShowNameSuggestions, "textEditor.csharp.intellisense.showNameCompletionSuggestions"). - Add(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, "textEditor.csharp.intellisense.showCompletionItemsFromUnimportedNamespaces"). - Add(CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, "textEditor.csharp.intellisense.enableArgumentCompletionSnippets"). - Add(CompletionOptionsStorage.ShowNewSnippetExperienceUserOption, "textEditor.csharp.intellisense.showNewSnippetExperience"); + Add(CompletionOptionsStorage.TriggerOnTypingLetters, "languages.csharp.intellisense.triggerCompletionOnTypingLetters"). + Add(CompletionOptionsStorage.TriggerOnDeletion, "languages.csharp.intellisense.triggerCompletionOnDeletion"). + Add(CompletionOptionsStorage.TriggerInArgumentLists, "languages.csharp.intellisense.triggerCompletionInArgumentLists"). + Add(CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems, "languages.csharp.intellisense.highlightMatchingPortionsOfCompletionListItems"). + Add(CompletionViewOptionsStorage.ShowCompletionItemFilters, "languages.csharp.intellisense.showCompletionItemFilters"). + Add(CompleteStatementOptionsStorage.AutomaticallyCompleteStatementOnSemicolon, "languages.csharp.intellisense.completeStatementOnSemicolon"). + Add(CompletionOptionsStorage.SnippetsBehavior, "languages.csharp.intellisense.snippetsBehavior"). + Add(CompletionOptionsStorage.EnterKeyBehavior, "languages.csharp.intellisense.returnKeyCompletionBehavior"). + Add(CompletionOptionsStorage.ShowNameSuggestions, "languages.csharp.intellisense.showNameCompletionSuggestions"). + Add(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, "languages.csharp.intellisense.showCompletionItemsFromUnimportedNamespaces"). + Add(CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, "languages.csharp.intellisense.enableArgumentCompletionSnippets"). + Add(CompletionOptionsStorage.ShowNewSnippetExperienceUserOption, "languages.csharp.intellisense.showNewSnippetExperience"); /// /// Array containing the option to expected unified settings for C# intellisense page. @@ -132,9 +132,8 @@ public async Task CSharpCategoriesTest() var categories = jsonDocument!.Root["categories"]!.AsObject(); var propertyToCategory = categories.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Deserialize()); Assert.Equal(2, propertyToCategory.Count); - Assert.Equal("C#", propertyToCategory["textEditor.csharp"]!.Title); - Assert.Equal("IntelliSense", propertyToCategory["textEditor.csharp.intellisense"]!.Title); - Assert.Equal(Guids.CSharpOptionPageIntelliSenseIdString, propertyToCategory["textEditor.csharp.intellisense"]!.LegacyOptionPageId); + Assert.Equal("C#", propertyToCategory["languages.csharp"]!.Title); + Assert.Equal("IntelliSense", propertyToCategory["languages.csharp.intellisense"]!.Title); await VerifyTagAsync(jsonDocument.ToString(), "Roslyn.VisualStudio.Next.UnitTests.csharpPackageRegistration.pkgdef"); } @@ -148,7 +147,7 @@ public async Task CSharpIntellisenseTest() Assert.True(s_csharpUnifiedSettingsStorage.ContainsKey(option)); } - VerifyProperties(jsonDocument!, "textEditor.csharp.intellisense", s_csharpIntellisenseExpectedSettings); + VerifyProperties(jsonDocument!, "languages.csharp.intellisense", s_csharpIntellisenseExpectedSettings); await VerifyTagAsync(jsonDocument!.ToString(), "Roslyn.VisualStudio.Next.UnitTests.csharpPackageRegistration.pkgdef"); } @@ -159,14 +158,14 @@ public async Task CSharpIntellisenseTest() /// Dictionary containing the option to unified setting path for VB. /// private static readonly ImmutableDictionary s_visualBasicUnifiedSettingsStorage = ImmutableDictionary.Empty. - Add(CompletionOptionsStorage.TriggerOnTypingLetters, "textEditor.basic.intellisense.triggerCompletionOnTypingLetters"). - Add(CompletionOptionsStorage.TriggerOnDeletion, "textEditor.basic.intellisense.triggerCompletionOnDeletion"). - Add(CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems, "textEditor.basic.intellisense.highlightMatchingPortionsOfCompletionListItems"). - Add(CompletionViewOptionsStorage.ShowCompletionItemFilters, "textEditor.basic.intellisense.showCompletionItemFilters"). - Add(CompletionOptionsStorage.SnippetsBehavior, "textEditor.basic.intellisense.snippetsBehavior"). - Add(CompletionOptionsStorage.EnterKeyBehavior, "textEditor.basic.intellisense.returnKeyCompletionBehavior"). - Add(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, "textEditor.basic.intellisense.showCompletionItemsFromUnimportedNamespaces"). - Add(CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, "textEditor.basic.intellisense.enableArgumentCompletionSnippets"); + Add(CompletionOptionsStorage.TriggerOnTypingLetters, "languages.basic.intellisense.triggerCompletionOnTypingLetters"). + Add(CompletionOptionsStorage.TriggerOnDeletion, "languages.basic.intellisense.triggerCompletionOnDeletion"). + Add(CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems, "languages.basic.intellisense.highlightMatchingPortionsOfCompletionListItems"). + Add(CompletionViewOptionsStorage.ShowCompletionItemFilters, "languages.basic.intellisense.showCompletionItemFilters"). + Add(CompletionOptionsStorage.SnippetsBehavior, "languages.basic.intellisense.snippetsBehavior"). + Add(CompletionOptionsStorage.EnterKeyBehavior, "languages.basic.intellisense.returnKeyCompletionBehavior"). + Add(CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, "languages.basic.intellisense.showCompletionItemsFromUnimportedNamespaces"). + Add(CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, "languages.basic.intellisense.enableArgumentCompletionSnippets"); /// /// Array containing the option to expected unified settings for VB intellisense page. @@ -233,9 +232,8 @@ public async Task VisualBasicCategoriesTest() var categories = jsonDocument!.Root["categories"]!.AsObject(); var propertyToCategory = categories.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Deserialize()); Assert.Equal(2, propertyToCategory.Count); - Assert.Equal("Visual Basic", propertyToCategory["textEditor.basic"]!.Title); - Assert.Equal("IntelliSense", propertyToCategory["textEditor.basic.intellisense"]!.Title); - Assert.Equal(Guids.VisualBasicOptionPageIntelliSenseIdString, propertyToCategory["textEditor.basic.intellisense"]!.LegacyOptionPageId); + Assert.Equal("Visual Basic", propertyToCategory["languages.basic"]!.Title); + Assert.Equal("IntelliSense", propertyToCategory["languages.basic.intellisense"]!.Title); await VerifyTagAsync(jsonDocument.ToString(), "Roslyn.VisualStudio.Next.UnitTests.visualBasicPackageRegistration.pkgdef"); } @@ -249,7 +247,7 @@ public async Task VisualBasicIntellisenseTest() Assert.True(s_visualBasicUnifiedSettingsStorage.ContainsKey(option)); } - VerifyProperties(jsonDocument!, "textEditor.basic.intellisense", s_visualBasicIntellisenseExpectedSettings); + VerifyProperties(jsonDocument!, "languages.basic.intellisense", s_visualBasicIntellisenseExpectedSettings); await VerifyTagAsync(jsonDocument!.ToString(), "Roslyn.VisualStudio.Next.UnitTests.visualBasicPackageRegistration.pkgdef"); } @@ -318,7 +316,7 @@ private static UnifiedSettingsOption CreateBooleanOption( // If the option default value is null, it means the option is in experiment mode and is hidden by a feature flag. // In Unified Settings it is not allowed and should be replaced by using the alternative default. // Like: - // "textEditor.csharp.intellisense.showNewSnippetExperience": { + // "languages.csharp.intellisense.showNewSnippetExperience": { // "type": "boolean", // "default": false, // "alternateDefault": { diff --git a/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index 0e574edcfeb..4afb4474e10 100644 --- a/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/roslyn/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -240,7 +240,7 @@ class { } ' confirm that IDE doesn't report the diagnostics Dim diagnostics = Await diagnosticService.GetDiagnosticsForIdsAsync( - document.Project, ImmutableArray.Create(document.Id), diagnosticIds:=Nothing, shouldIncludeAnalyzer:=Nothing, + document.Project, ImmutableArray.Create(document.Id), diagnosticIds:=Nothing, AnalyzerFilter.All, includeLocalDocumentDiagnostics:=True, CancellationToken.None) Assert.False(diagnostics.Any()) End Using diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs index 8be52df7103..c290086e118 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Editor/Implementation/Debugging/FSharpBreakpointResolutionResult.cs @@ -21,8 +21,8 @@ private FSharpBreakpointResolutionResult(BreakpointResolutionResult result) public bool IsLineBreakpoint => UnderlyingObject.IsLineBreakpoint; public static FSharpBreakpointResolutionResult CreateSpanResult(Document document, TextSpan textSpan, string? locationNameOpt = null) - => new FSharpBreakpointResolutionResult(BreakpointResolutionResult.CreateSpanResult(document, textSpan, locationNameOpt)); + => new(BreakpointResolutionResult.CreateSpanResult(document, textSpan, locationNameOpt)); public static FSharpBreakpointResolutionResult CreateLineResult(Document document, string? locationNameOpt = null) - => new FSharpBreakpointResolutionResult(BreakpointResolutionResult.CreateLineResult(document, locationNameOpt)); + => new(BreakpointResolutionResult.CreateLineResult(document, locationNameOpt)); } diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs index 100cd2c1df0..98398188903 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs @@ -39,7 +39,7 @@ public Task> AnalyzeSemanticsAsync(DiagnosticDescript internal class FSharpSimplifyNameDiagnosticAnalyzer : DocumentDiagnosticAnalyzer, IBuiltInAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor( + new( IDEDiagnosticIds.SimplifyNamesDiagnosticId, ExternalAccessFSharpResources.SimplifyName, ExternalAccessFSharpResources.NameCanBeSimplified, diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs index 95ee909b74c..bde4d5ba30a 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs @@ -41,7 +41,7 @@ internal class FSharpUnusedDeclarationsDiagnosticAnalyzer : DocumentDiagnosticAn private const string DescriptorId = IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId; private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor( + new( DescriptorId, ExternalAccessFSharpResources.TheValueIsUnused, ExternalAccessFSharpResources.TheValueIsUnused, diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs index fcb8558fcd5..71267b85282 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedOpensDiagnosticAnalyzer.cs @@ -39,7 +39,7 @@ public Task> AnalyzeSemanticsAsync(DiagnosticDescript internal class FSharpUnusedOpensDeclarationsDiagnosticAnalyzer : DocumentDiagnosticAnalyzer { private readonly DiagnosticDescriptor _descriptor = - new DiagnosticDescriptor( + new( IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId, ExternalAccessFSharpResources.RemoveUnusedOpens, ExternalAccessFSharpResources.UnusedOpens, diff --git a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpProvider.cs b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpProvider.cs index 6c4fd7505e3..c6a503f7769 100644 --- a/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpProvider.cs +++ b/src/roslyn/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpProvider.cs @@ -5,12 +5,12 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.SignatureHelp; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.SignatureHelp; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.SignatureHelp; @@ -61,23 +61,23 @@ public FSharpSignatureHelpProvider( if (mappedSignatureHelpItems != null) { return new SignatureHelpItems( - mappedSignatureHelpItems.Items.Select(x => + mappedSignatureHelpItems.Items.SelectAsArray(x => new SignatureHelpItem( x.IsVariadic, x.DocumentationFactory, x.PrefixDisplayParts, x.SeparatorDisplayParts, x.SuffixDisplayParts, - x.Parameters.Select(y => + x.Parameters.SelectAsArray(y => new SignatureHelpParameter( y.Name, y.IsOptional, y.DocumentationFactory, - y.DisplayParts, - y.PrefixDisplayParts, - y.SuffixDisplayParts, - y.SelectedDisplayParts)).ToList(), - x.DescriptionParts)).ToList(), + y.DisplayParts.ToImmutableArrayOrEmpty(), + y.PrefixDisplayParts.ToImmutableArrayOrEmpty(), + y.SuffixDisplayParts.ToImmutableArrayOrEmpty(), + y.SelectedDisplayParts.ToImmutableArrayOrEmpty())), + x.DescriptionParts)), mappedSignatureHelpItems.ApplicableSpan, mappedSignatureHelpItems.ArgumentIndex, mappedSignatureHelpItems.ArgumentCount, diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs index 18f0a4e1455..b75f26158a7 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToImplementation.cs @@ -119,9 +119,7 @@ interface IBar } [IdeTheory] - //[CombinatorialData] - [InlineData(false, Skip = "https://github.com/dotnet/roslyn/issues/77293")] - [InlineData(true)] + [CombinatorialData] public async Task GoToImplementationFromMetadataAsSource(bool asyncNavigation) { await TestServices.Editor.ConfigureAsyncNavigation(asyncNavigation ? AsyncNavigationKind.Asynchronous : AsyncNavigationKind.Synchronous, HangMitigatingCancellationToken); diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpReplIdeFeatures.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpReplIdeFeatures.cs index ac5036da0f0..90419a6f50a 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpReplIdeFeatures.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpReplIdeFeatures.cs @@ -85,7 +85,7 @@ public async Task HighlightRefsSingleSubmissionVerifyRenameTagsGoAway() } [IdeFact] - public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpWhenInvokedOnSubmittedText() + public async Task HighlightRefsMultipleSubmissionsVerifyRenameTagsShowUpWhenInvokedOnSubmittedText() { await TestServices.InteractiveWindow.SubmitTextAsync("class Goo { }", HangMitigatingCancellationToken); await TestServices.InteractiveWindow.SubmitTextAsync("Goo something = new Goo();", HangMitigatingCancellationToken); @@ -97,7 +97,7 @@ public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpWhenInvok } [IdeFact] - public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpOnUnsubmittedText() + public async Task HighlightRefsMultipleSubmissionsVerifyRenameTagsShowUpOnUnsubmittedText() { await TestServices.InteractiveWindow.SubmitTextAsync("class Goo { }", HangMitigatingCancellationToken); await TestServices.InteractiveWindow.SubmitTextAsync("Goo something = new Goo();", HangMitigatingCancellationToken); @@ -109,7 +109,7 @@ public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpOnUnsubmi } [IdeFact] - public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpOnTypesWhenInvokedOnSubmittedText() + public async Task HighlightRefsMultipleSubmissionsVerifyRenameTagsShowUpOnTypesWhenInvokedOnSubmittedText() { await TestServices.InteractiveWindow.SubmitTextAsync("class Goo { }", HangMitigatingCancellationToken); await TestServices.InteractiveWindow.SubmitTextAsync("Goo a;", HangMitigatingCancellationToken); @@ -121,7 +121,7 @@ public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpOnTypesWh } [IdeFact] - public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpOnTypesWhenInvokedOnUnsubmittedText() + public async Task HighlightRefsMultipleSubmissionsVerifyRenameTagsShowUpOnTypesWhenInvokedOnUnsubmittedText() { await TestServices.InteractiveWindow.SubmitTextAsync("class Goo { }", HangMitigatingCancellationToken); await TestServices.InteractiveWindow.SubmitTextAsync("Goo a;", HangMitigatingCancellationToken); @@ -133,7 +133,7 @@ public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsShowUpOnTypesWh } [IdeFact] - public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsGoAwayWhenInvokedOnUnsubmittedText() + public async Task HighlightRefsMultipleSubmissionsVerifyRenameTagsGoAwayWhenInvokedOnUnsubmittedText() { await TestServices.InteractiveWindow.SubmitTextAsync("class Goo { }", HangMitigatingCancellationToken); await TestServices.InteractiveWindow.SubmitTextAsync("Goo a;", HangMitigatingCancellationToken); @@ -145,7 +145,7 @@ public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsGoAwayWhenInvok } [IdeFact] - public async Task HighlightRefsMultipleSubmisionsVerifyRenameTagsOnRedefinedVariable() + public async Task HighlightRefsMultipleSubmissionsVerifyRenameTagsOnRedefinedVariable() { await TestServices.InteractiveWindow.SubmitTextAsync("string abc = null;", HangMitigatingCancellationToken); await TestServices.InteractiveWindow.SubmitTextAsync("abc = string.Empty;", HangMitigatingCancellationToken); diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpTempPE.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpTempPE.cs new file mode 100644 index 00000000000..a1c0561b632 --- /dev/null +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpTempPE.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop; +using Microsoft.VisualStudio.Shell; +using Roslyn.VisualStudio.IntegrationTests; +using Xunit; + +namespace Roslyn.VisualStudio.NewIntegrationTests.CSharp; + +public sealed class CSharpTempPE : AbstractIntegrationTest +{ + [IdeFact] + public async Task TempPEServiceWorks() + { + using TempRoot root = new(); + var outputPath = root.CreateFile().Path; + + var service = await AsyncServiceProvider.GlobalProvider.GetServiceAsync(); + var result = service.CompileTempPE(outputPath, 1, ["Test.cs"], ["class Program { static void Main() { } }"], 0, [], []); + Assert.Equal(VSConstants.S_OK, result); + + Assert.True(File.Exists(outputPath)); + } +} diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpWinForms.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpWinForms.cs index 07ddd881d08..ae39373263e 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpWinForms.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpWinForms.cs @@ -115,7 +115,7 @@ private void ExecuteWhenButtonClicked(object sender, EventArgs e) """, codeFileActualText); } - [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/77293")] + [IdeFact] public async Task RenameControl() { var project = ProjectName; @@ -152,7 +152,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( Assert.DoesNotContain(@"private System.Windows.Forms.Button SomeButton;", actualText); } - [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/77293")] + [IdeFact] public async Task RemoveEventHandler() { var project = ProjectName; @@ -182,7 +182,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( Assert.DoesNotContain(@"VisualStudio.Editor.SomeButton.Click += new System.EventHandler(VisualStudio.Editor.GooHandler);", actualText); } - [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/77293")] + [IdeFact] public async Task ChangeAccessibility() { var project = ProjectName; @@ -211,7 +211,7 @@ await TestServices.Workspace.WaitForAllAsyncOperationsAsync( var actualText = await TestServices.Editor.GetTextAsync(HangMitigatingCancellationToken); Assert.Contains(@"public System.Windows.Forms.Button SomeButton;", actualText); } - [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/77293")] + [IdeFact] public async Task DeleteControl() { if (ExecutionConditionUtil.IsBitness64) diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 3636689ba17..af2ae5490e0 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -580,18 +580,18 @@ public async Task IsNavigationBarEnabledAsync(CancellationToken cancellati return (await GetNavigationBarMarginAsync(view, cancellationToken)) is not null; } - private async Task> GetNavigationBarComboBoxesAsync(IWpfTextView textView, CancellationToken cancellationToken) + private async Task> GetNavigationBarComboBoxesAsync(IWpfTextView textView, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var margin = await GetNavigationBarMarginAsync(textView, cancellationToken); try { - return margin.GetFieldValue>("_combos"); + return margin.GetFieldValue>("_combos"); } catch (FieldAccessException) { - return margin.GetFieldValue>("Combos"); + return margin.GetFieldValue>("Combos"); } } @@ -842,13 +842,12 @@ private Func> GetLightBulbApplicatio action = fixAllAction; if (willBlockUntilComplete - && action is AbstractFixAllSuggestedAction fixAllSuggestedAction - && fixAllSuggestedAction.CodeAction is AbstractFixAllCodeAction fixAllCodeAction) + && action is EditorSuggestedActionForRefactorOrFixAll fixAllSuggestedAction) { // Ensure the preview changes dialog will not be shown. Since the operation 'willBlockUntilComplete', // the caller would not be able to interact with the preview changes dialog, and the tests would // either timeout or deadlock. - fixAllCodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; + fixAllSuggestedAction.CodeAction.GetTestAccessor().ShowPreviewChangesDialog = false; } if (string.IsNullOrEmpty(actionName)) @@ -860,7 +859,7 @@ private Func> GetLightBulbApplicatio broker.DismissSession(view); } - if (action is not SuggestedAction suggestedAction) + if (action is not EditorSuggestedAction suggestedAction) return true; broker.DismissSession(view); @@ -933,7 +932,7 @@ private async Task> SelectActionsAsync(IEnumerable return actions; } - private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) + private async Task GetFixAllSuggestedActionAsync(IEnumerable actionSets, FixAllScope fixAllScope, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -941,13 +940,10 @@ private async Task> SelectActionsAsync(IEnumerable { foreach (var action in actionSet.Actions) { - if (action is AbstractFixAllSuggestedAction fixAllSuggestedAction) + if (action is EditorSuggestedActionForRefactorOrFixAll fixAllSuggestedAction && + fixAllSuggestedAction.CodeAction.RefactorOrFixAllState.Scope == fixAllScope) { - var fixAllCodeAction = fixAllSuggestedAction.CodeAction as AbstractFixAllCodeAction; - if (fixAllCodeAction?.FixAllState?.Scope == fixAllScope) - { - return fixAllSuggestedAction; - } + return fixAllSuggestedAction; } if (action.HasActionSets) diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs index 91d7d95dd13..c7a66d0ce03 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs @@ -17,6 +17,7 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; +using Roslyn.Test.Utilities; using Xunit; using IAsyncDisposable = System.IAsyncDisposable; @@ -100,7 +101,7 @@ private async Task ExecuteRemotableCommandAsync(string commandName, Cancellation ErrorHandler.ThrowOnFailure(commandService.GetControlDataSourceAsync( (uint)__VSCOMMANDTYPES.cCommandTypeButton, commandName, - timeout: 0, + timeout: ((int)TestHelpers.HangMitigatingTimeout.TotalMilliseconds), out var dataSourceTask)); Assumes.NotNull(dataSourceTask); diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index d05be9ee625..dff4e73e3c4 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -203,12 +203,12 @@ private async Task CreateSolutionAsync(string solutionPath, string solutionName, return references; } - public async Task GetProjectReferencesAsync(string projectName, CancellationToken cancellationToken) + public async Task> GetProjectReferencesAsync(string projectName, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var project = await GetProjectAsync(projectName, cancellationToken); - var references = ((VSProject)project.Object).References.Cast().Where(x => x.SourceProject != null).Select(x => x.Name).ToArray(); + var references = ((VSProject)project.Object).References.Cast().SelectAsArray(x => x.SourceProject != null, x => x.Name); return references; } diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj index 2bf4f13ced4..ae37c6137d6 100644 --- a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/Microsoft.VisualStudio.LanguageServices.New.IntegrationTests.csproj @@ -54,6 +54,7 @@ + diff --git a/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicTempPE.cs b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicTempPE.cs new file mode 100644 index 00000000000..3ff52ca419e --- /dev/null +++ b/src/roslyn/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicTempPE.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim.Interop; +using Microsoft.VisualStudio.Shell; +using Roslyn.VisualStudio.IntegrationTests; +using Xunit; + +namespace Roslyn.VisualStudio.NewIntegrationTests.VisualBasic; + +public sealed class BasicTempPE : AbstractIntegrationTest +{ + [IdeFact] + public async Task TempPEServiceWorks() + { + using TempRoot root = new(); + var outputPath = root.CreateFile().Path; + + var factory = await AsyncServiceProvider.GlobalProvider.GetServiceAsync(); + var compiler = factory.CreateCompiler(); + + Assert.NotNull(compiler); + + // Create a temporary project to test compilation + var project = compiler.CreateProject("TestProject", null, null, new VbCompilerHost()); + Assert.NotNull(project); + + var sourceFile = root.CreateFile("Test.vb").WriteAllText("Module Program\r\n Sub Main()\r\n End Sub\r\nEnd Module").Path; + project.AddFile(sourceFile, (uint)VSConstants.VSITEMID.Nil, fAddDuringOpen: false); + + project.SetCompilerOptions(new VBCompilerOptions() { wszOutputPath = Path.GetDirectoryName(outputPath), wszExeName = Path.GetFileName(outputPath) }); + + // Compile the project + var result = compiler.Compile(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + Assert.Equal(VSConstants.S_OK, result); + + Assert.True(File.Exists(outputPath)); + } + + private class VbCompilerHost : IVbCompilerHost + { + public void OutputString(string @string) + { + } + + public int GetSdkPath(out string sdkPath) + { + sdkPath = ""; + return VSConstants.E_NOTIMPL; + } + + public VBTargetLibraryType GetTargetLibraryType() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index bb155d68322..83b8e30733b 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -44,7 +44,7 @@ internal sealed class RemoteLanguageServiceWorkspace : CodeAnalysis.Workspace, I /// // Our usage of SemaphoreSlim is fine. We don't perform blocking waits for it on the UI thread. #pragma warning disable RS0030 // Do not use banned APIs - private static readonly SemaphoreSlim s_RemotePathsGate = new SemaphoreSlim(initialCount: 1); + private static readonly SemaphoreSlim s_RemotePathsGate = new(initialCount: 1); #pragma warning restore RS0030 // Do not use banned APIs private readonly IServiceProvider _serviceProvider; diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs index c52b9ef3f54..f2fef6e95a3 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspaceHost.cs @@ -36,7 +36,7 @@ internal sealed class RemoteLanguageServiceWorkspaceHost : ICollaborationService // A collection of loaded Roslyn Project IDs, indexed by project path. private ImmutableDictionary _loadedProjects = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); private ImmutableDictionary _loadedProjectInfo = ImmutableDictionary.Create(StringComparer.OrdinalIgnoreCase); - private TaskCompletionSource _projectsLoadedTaskCompletionSource = new TaskCompletionSource(); + private TaskCompletionSource _projectsLoadedTaskCompletionSource = new(); private readonly RemoteProjectInfoProvider _remoteProjectInfoProvider; private readonly SVsServiceProvider _serviceProvider; diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs index 414dcd10196..0fd054606c7 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/CustomProtocol/LspRequestExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Protocol; public static class LspRequestExtensions { internal static LS.LspRequest ToLSRequest(this LSP.LspRequest lspRequest) - => new LS.LspRequest(lspRequest.Name); + => new(lspRequest.Name); internal static LSP.ClientCapabilities GetClientCapabilities(this LS.RequestContext requestContext) => requestContext.ClientCapabilities?.ToObject() ?? new LSP.VSInternalClientCapabilities(); diff --git a/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs b/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs index 4a8d2406b85..73780d3c8b3 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Impl/LiveShareInitializeHandler.cs @@ -15,7 +15,7 @@ namespace Microsoft.VisualStudio.LanguageServices.LiveShare.Shims; internal class LiveShareInitializeHandler : ILspRequestHandler { - private static readonly InitializeResult s_initializeResult = new InitializeResult + private static readonly InitializeResult s_initializeResult = new() { Capabilities = new ServerCapabilities { diff --git a/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs b/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs index 775506ade8f..608653e1fec 100644 --- a/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs +++ b/src/roslyn/src/VisualStudio/LiveShare/Test/ProjectsHandlerTests.cs @@ -31,7 +31,7 @@ public async Task TestProjectsAsync(bool mutatingLspWorkspace) } private static CustomProtocol.Project CreateLspProject(Project project) - => new CustomProtocol.Project() + => new() { Language = project.Language, Name = project.Name, diff --git a/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb b/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb index 68dde06460c..4cbf4161484 100644 --- a/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb +++ b/src/roslyn/src/VisualStudio/TestUtilities2/MockServiceProvider.vb @@ -4,6 +4,7 @@ Imports System.ComponentModel.Composition Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.Internal.VisualStudio.Shell.Interop Imports Microsoft.VisualStudio.ComponentModelHost Imports Microsoft.VisualStudio.Shell Imports Microsoft.VisualStudio.Shell.Interop @@ -25,7 +26,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Private ReadOnly _exportProvider As Composition.ExportProvider Private ReadOnly _fileChangeEx As New MockVsFileChangeEx - Public MockMonitorSelection As IVsMonitorSelection Public MockRunningDocumentTable As New MockVsRunningDocumentTable @@ -41,12 +41,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Dim solutionMock As New Mock(Of IVsSolution2)(MockBehavior.Loose) Return solutionMock.Object + Case GetType(SVsBackgroundSolution) + Return GetVsBackgroundSolutionMock() + Case GetType(SComponentModel) Return GetComponentModelMock() - Case GetType(SVsShellMonitorSelection) - Return MockMonitorSelection - Case GetType(SVsXMLMemberIndexService) Return New MockXmlMemberIndexService @@ -79,6 +79,19 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests Friend Function GetComponentModelMock() As IComponentModel Return New MockComponentModel(_exportProvider) End Function + + Private Shared Function GetVsBackgroundSolutionMock() As IVsBackgroundSolution + ' Return a simple mock that lets callers subscribe to events + Dim mock = New Mock(Of IVsBackgroundSolution)(MockBehavior.Strict) + + mock.Setup(Function(s) s.SubscribeListener(It.IsAny(Of Object))).Returns( + Function() + Return New Mock(Of IVsBackgroundDisposable)().Object + End Function) + mock.SetupGet(Function(s) s.IsSolutionOpening).Returns(False) + + Return mock.Object + End Function End Class End Namespace diff --git a/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb b/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb index e2fcf746a96..5967e4786e9 100644 --- a/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb +++ b/src/roslyn/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb @@ -85,9 +85,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr ThreadingContext = ExportProvider.GetExportedValue(Of IThreadingContext)() Implementation.Interop.WrapperPolicy.s_ComWrapperFactory = MockComWrapperFactory.Instance - Dim mockServiceProvider As MockServiceProvider = ExportProvider.GetExportedValue(Of MockServiceProvider)() - mockServiceProvider.MockMonitorSelection = New MockShellMonitorSelection(True) - ServiceProvider = mockServiceProvider + ServiceProvider = ExportProvider.GetExportedValue(Of MockServiceProvider)() End Sub Public ReadOnly Property ProjectFactory As VisualStudioProjectFactory @@ -170,46 +168,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr Return Workspace.CurrentSolution.Projects.Single().CompilationOptions End Function - Private Class MockShellMonitorSelection - Implements IVsMonitorSelection - - Public Property SolutionIsFullyLoaded As Boolean - - Public Sub New(solutionIsFullyLoaded As Boolean) - Me.SolutionIsFullyLoaded = solutionIsFullyLoaded - End Sub - - Public Function AdviseSelectionEvents(pSink As IVsSelectionEvents, ByRef pdwCookie As UInteger) As Integer Implements IVsMonitorSelection.AdviseSelectionEvents - Return VSConstants.S_OK - End Function - - Public Function GetCmdUIContextCookie( ByRef rguidCmdUI As Guid, ByRef pdwCmdUICookie As UInteger) As Integer Implements IVsMonitorSelection.GetCmdUIContextCookie - Return VSConstants.S_OK - End Function - - Public Function GetCurrentElementValue( elementid As UInteger, ByRef pvarValue As Object) As Integer Implements IVsMonitorSelection.GetCurrentElementValue - Throw New NotImplementedException() - End Function - - Public Function GetCurrentSelection(ByRef ppHier As IntPtr, ByRef pitemid As UInteger, ByRef ppMIS As IVsMultiItemSelect, ByRef ppSC As IntPtr) As Integer Implements IVsMonitorSelection.GetCurrentSelection - Throw New NotImplementedException() - End Function - - Public Function IsCmdUIContextActive( dwCmdUICookie As UInteger, ByRef pfActive As Integer) As Integer Implements IVsMonitorSelection.IsCmdUIContextActive - ' Be lazy and don't worry checking which cookie this is, since for now the VisualStudioProjectTracker only checks for one - pfActive = If(SolutionIsFullyLoaded, 1, 0) - Return VSConstants.S_OK - End Function - - Public Function SetCmdUIContext( dwCmdUICookie As UInteger, fActive As Integer) As Integer Implements IVsMonitorSelection.SetCmdUIContext - Throw New NotImplementedException() - End Function - - Public Function UnadviseSelectionEvents( dwCookie As UInteger) As Integer Implements IVsMonitorSelection.UnadviseSelectionEvents - Throw New NotImplementedException() - End Function - End Class - Friend Async Function GetFileChangeServiceAsync() As Task(Of MockVsFileChangeEx) ' Ensure we've pushed everything to the file change watcher Dim fileChangeProvider = ExportProvider.GetExportedValue(Of FileChangeWatcherProvider) diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb b/src/roslyn/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb index 90f17e9234e..3ebc391763a 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb @@ -4,6 +4,7 @@ Imports System.ComponentModel.Design Imports System.Runtime.InteropServices +Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ErrorReporting Imports Microsoft.CodeAnalysis.Options @@ -31,12 +32,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic ' Code Style (category) ' General ' Naming - ' IntelliSense - @@ -59,7 +58,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Public Sub New() MyBase.New() - ' This is a UI-affinitized operation. Currently this opeartion prevents setting AllowsBackgroundLoad for + ' This is a UI-affinitized operation. Currently this operation prevents setting AllowsBackgroundLoad for ' VisualBasicPackage. The call should be removed from the constructor, and the package set back to allowing ' background loads. _comAggregate = Implementation.Interop.ComAggregate.CreateAggregatedObject(Me) @@ -73,11 +72,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic isMainThreadTask:=False, task:=Function() As Task Try - RegisterLanguageService(GetType(IVbCompilerService), Function() Task.FromResult(_comAggregate)) + AddService(GetType(IVbCompilerService), Function(_1, cancellationToken, _2) + PreloadProjectSystemComponents() + Return Task.FromResult(_comAggregate) + End Function, promote:=True) DirectCast(Me, IServiceContainer).AddService( GetType(IVbTempPECompilerFactory), - Function(_1, _2) New TempPECompilerFactory(Me.ComponentModel.GetService(Of VisualStudioWorkspace)())) + Function(_1, _2) New TempPECompilerFactory(Me.ComponentModel.GetService(Of VisualStudioWorkspace)()), + promote:=True) Catch ex As Exception When FatalError.ReportAndPropagateUnlessCanceled(ex) Throw ExceptionUtilities.Unreachable End Try diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj b/src/roslyn/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj index 3dd19da1458..29240139e1d 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/Microsoft.VisualStudio.LanguageServices.VisualBasic.vbproj @@ -47,6 +47,7 @@ + diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPage.vb b/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPage.vb deleted file mode 100644 index 0a760a499e8..00000000000 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPage.vb +++ /dev/null @@ -1,16 +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. - -Imports System.Runtime.InteropServices -Imports Microsoft.VisualStudio.LanguageServices.Implementation.Options - -Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options - - Friend Class IntelliSenseOptionPage - Inherits AbstractOptionPage - Protected Overrides Function CreateOptionPage(serviceProvider As IServiceProvider, optionStore As OptionStore) As AbstractOptionPageControl - Return New IntelliSenseOptionPageControl(optionStore) - End Function - End Class -End Namespace diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml b/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml deleted file mode 100644 index cdc81d010c0..00000000000 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb b/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb deleted file mode 100644 index 7a28520e808..00000000000 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageControl.xaml.vb +++ /dev/null @@ -1,51 +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. - -Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Completion -Imports Microsoft.VisualStudio.LanguageServices.Implementation.Options - -Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options - Partial Friend Class IntelliSenseOptionPageControl - Inherits AbstractOptionPageControl - - Public Sub New(optionStore As OptionStore) - MyBase.New(optionStore) - InitializeComponent() - - BindToOption(Show_completion_list_after_a_character_is_typed, CompletionOptionsStorage.TriggerOnTypingLetters, LanguageNames.VisualBasic) - BindToOption(Show_completion_list_after_a_character_is_deleted, CompletionOptionsStorage.TriggerOnDeletion, LanguageNames.VisualBasic, onNullValue:=function() True) - - BindToOption(Highlight_matching_portions_of_completion_list_items, CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems, LanguageNames.VisualBasic) - BindToOption(Show_completion_item_filters, CompletionViewOptionsStorage.ShowCompletionItemFilters, LanguageNames.VisualBasic) - - BindToOption(Never_include_snippets, CompletionOptionsStorage.SnippetsBehavior, SnippetsRule.NeverInclude, LanguageNames.VisualBasic) - BindToOption(Always_include_snippets, CompletionOptionsStorage.SnippetsBehavior, SnippetsRule.AlwaysInclude, LanguageNames.VisualBasic) - BindToOption(Include_snippets_when_question_Tab_is_typed_after_an_identifier, CompletionOptionsStorage.SnippetsBehavior, SnippetsRule.IncludeAfterTypingIdentifierQuestionTab, LanguageNames.VisualBasic) - SetSnippetDefaultBehavior() - - BindToOption(Never_add_new_line_on_enter, CompletionOptionsStorage.EnterKeyBehavior, EnterKeyRule.Never, LanguageNames.VisualBasic) - BindToOption(Only_add_new_line_on_enter_with_whole_word, CompletionOptionsStorage.EnterKeyBehavior, EnterKeyRule.AfterFullyTypedWord, LanguageNames.VisualBasic) - BindToOption(Always_add_new_line_on_enter, CompletionOptionsStorage.EnterKeyBehavior, EnterKeyRule.Always, LanguageNames.VisualBasic) - SetEnterKeyDefaultBehavior() - - BindToOption(Show_items_from_unimported_namespaces, CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, LanguageNames.VisualBasic, onNullValue:=function() True) - BindToOption(Tab_twice_to_insert_arguments, CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, LanguageNames.VisualBasic, onNullValue:=function() False) - End Sub - - Private Sub SetSnippetDefaultBehavior() - Dim snippetValue = Me.OptionStore.GetOption(CompletionOptionsStorage.SnippetsBehavior, LanguageNames.VisualBasic) - If snippetValue = SnippetsRule.Default Then - Include_snippets_when_question_Tab_is_typed_after_an_identifier.IsChecked = True - End If - End Sub - - Private Sub SetEnterKeyDefaultBehavior() - Dim enterKeyRule = Me.OptionStore.GetOption(CompletionOptionsStorage.EnterKeyBehavior, LanguageNames.VisualBasic) - If enterKeyRule = EnterKeyRule.Default Then - Always_add_new_line_on_enter.IsChecked = True - End If - End Sub - End Class -End Namespace diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb b/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb deleted file mode 100644 index 67a8b73d861..00000000000 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/Options/IntelliSenseOptionPageStrings.vb +++ /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 Microsoft.VisualStudio.LanguageServices.VisualBasic.Options - Friend Module IntelliSenseOptionPageStrings - Public ReadOnly Property Option_CompletionLists As String = - BasicVSResources.Completion_Lists - - Public ReadOnly Property Option_Show_completion_list_after_a_character_is_typed As String = - ServicesVSResources._Show_completion_list_after_a_character_is_typed - - Public ReadOnly Property Option_Show_completion_list_after_a_character_is_deleted As String = - ServicesVSResources.Show_completion_list_after_a_character_is__deleted - - Public ReadOnly Property Option_Highlight_matching_portions_of_completion_list_items As String = - ServicesVSResources._Highlight_matching_portions_of_completion_list_items - - Public ReadOnly Property Option_Show_completion_item_filters As String = - ServicesVSResources.Show_completion_item__filters - - Public ReadOnly Property Option_Only_add_new_line_on_enter_with_whole_word As String = - ServicesVSResources.Only_add_new_line_on_enter_after_end_of_fully_typed_word - - Public ReadOnly Property Option_Always_add_new_line_on_enter As String = - ServicesVSResources.Always_add_new_line_on_enter - - Public ReadOnly Property Option_Never_add_new_line_on_enter As String = - ServicesVSResources.Never_add_new_line_on_enter - - Public ReadOnly Property Enter_key_behavior_Title As String = - ServicesVSResources.Enter_key_behavior_colon - - Public ReadOnly Property Snippets_behavior As String = - ServicesVSResources.Snippets_behavior - - Public ReadOnly Property Option_Never_include_snippets As String = - VSPackage.Never_include_snippets - - Public ReadOnly Property Option_Always_include_snippets As String = - VSPackage.Always_include_snippets - - Public ReadOnly Property Option_Include_snippets_when_question_Tab_is_typed_after_an_identifier As String = - VSPackage.Include_snippets_when_Tab_is_typed_after_an_identifier - - Public ReadOnly Property Option_Show_items_from_unimported_namespaces As String = - ServicesVSResources.Show_items_from_unimported_namespaces - - Public ReadOnly Property Option_Tab_twice_to_insert_arguments_experimental As String = - ServicesVSResources.Tab_twice_to_insert_arguments_experimental - End Module -End Namespace diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef b/src/roslyn/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef index a0c8dc331b2..40b78b08e1e 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef @@ -32,6 +32,13 @@ "Name"="Visual Basic Project System Shim" "IsAsyncQueryable"=dword:00000001 +[$RootKey$\Services\{8df9750d-069b-4b81-973a-152e97420c5c}] +@="{574fc912-f74f-4b4e-92c3-f695c208a2bb}" +"Name"="Visual Basic TempPE Compiler Factory Service" +"IsAsyncQueryable"=dword:00000000 +"IsCacheable"=dword:00000001 +"IsFreeThreaded"=dword:00000001 + [$RootKey$\AutomationProperties\TextEditor\Basic-Specific] @="#104" "Description"="#106" @@ -60,14 +67,6 @@ // Code Style (category) // General // Naming -// IntelliSense - - -[$RootKey$\Languages\Language Services\Basic\EditorToolsOptions\IntelliSense] -@="#112" -"Package"="{574fc912-f74f-4b4e-92c3-f695c208a2bb}" -"Page"="{04460a3b-1b5f-4402-bc6d-89a4f6f0a8d7}" -"Keywords"="#312" [$RootKey$\Languages\Language Services\Basic\EditorToolsOptions\Code Style\General] @="#111" @@ -190,4 +189,4 @@ [$RootKey$\SettingsManifests\{574fc912-f74f-4b4e-92c3-f695c208a2bb}] @="Microsoft.VisualStudio.LanguageServices.VisualBasic.VisualBasicPackage" "ManifestPath"="$PackageFolder$\UnifiedSettings\visualBasicSettings.registration.json" -"CacheTag"=qword:A051BA2C3046E62E +"CacheTag"=qword:1717B568132C1E1B diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/UnifiedSettings/visualBasicSettings.registration.json b/src/roslyn/src/VisualStudio/VisualBasic/Impl/UnifiedSettings/visualBasicSettings.registration.json index 8751cfb2f07..21d6488396e 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/UnifiedSettings/visualBasicSettings.registration.json +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/UnifiedSettings/visualBasicSettings.registration.json @@ -5,7 +5,7 @@ { "properties": { // CompletionOptionsStorage.TriggerOnTypingLetters - "textEditor.basic.intellisense.triggerCompletionOnTypingLetters": { + "languages.basic.intellisense.triggerCompletionOnTypingLetters": { "title": "@Show_completion_list_after_a_character_is_typed;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -20,7 +20,7 @@ } }, // CompletionOptionsStorage.TriggerOnDeletion - "textEditor.basic.intellisense.triggerCompletionOnDeletion": { + "languages.basic.intellisense.triggerCompletionOnDeletion": { "title": "@Show_completion_list_after_a_character_is_deleted;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -35,7 +35,7 @@ } }, // CompletionViewOptionsStorage.HighlightMatchingPortionsOfCompletionListItems - "textEditor.basic.intellisense.highlightMatchingPortionsOfCompletionListItems": { + "languages.basic.intellisense.highlightMatchingPortionsOfCompletionListItems": { "title": "@Highlight_matching_portions_of_completion_list_items;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -50,7 +50,7 @@ } }, // CompletionViewOptionsStorage.ShowCompletionItemFilters - "textEditor.basic.intellisense.showCompletionItemFilters": { + "languages.basic.intellisense.showCompletionItemFilters": { "title": "@Show_completion_item_filters;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -65,7 +65,7 @@ } }, // CompletionOptionsStorage.SnippetsBehavior - "textEditor.basic.intellisense.snippetsBehavior": { + "languages.basic.intellisense.snippetsBehavior": { "title": "@Snippets_behavior;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "string", "enum": [ "neverInclude", "alwaysInclude", "includeAfterTypingIdentifierQuestionTab" ], @@ -104,7 +104,7 @@ } }, // CompletionOptionsStorage.EnterKeyBehavior - "textEditor.basic.intellisense.returnKeyCompletionBehavior": { + "languages.basic.intellisense.returnKeyCompletionBehavior": { "title": "@Enter_key_behavior;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "string", "enum": [ "never", "afterFullyTypedWord", "always" ], @@ -143,7 +143,7 @@ } }, // CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces - "textEditor.basic.intellisense.showCompletionItemsFromUnimportedNamespaces": { + "languages.basic.intellisense.showCompletionItemsFromUnimportedNamespaces": { "title": "@Show_items_from_unimported_namespaces;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": true, @@ -158,7 +158,7 @@ } }, // CompletionViewOptionsStorage.EnableArgumentCompletionSnippets - "textEditor.basic.intellisense.enableArgumentCompletionSnippets": { + "languages.basic.intellisense.enableArgumentCompletionSnippets": { "title": "@Tab_twice_to_insert_arguments;..\\Microsoft.VisualStudio.LanguageServices.dll", "type": "boolean", "default": false, @@ -179,12 +179,11 @@ } }, "categories": { - "textEditor.basic":{ + "languages.basic":{ "title": "@101;{574fc912-f74f-4b4e-92c3-f695c208a2bb}" }, - "textEditor.basic.intellisense": { - "title": "@112;{574fc912-f74f-4b4e-92c3-f695c208a2bb}", - "legacyOptionPageId": "04460A3B-1B5F-4402-BC6D-89A4F6F0A8D7" + "languages.basic.intellisense": { + "title": "@112;{574fc912-f74f-4b4e-92c3-f695c208a2bb}" } } } diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/VSPackage.resx b/src/roslyn/src/VisualStudio/VisualBasic/Impl/VSPackage.resx index 00e7dc9bbac..8251db54b0a 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/VSPackage.resx +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/VSPackage.resx @@ -196,10 +196,6 @@ Use enhanced colors;Editor Color Scheme;Inheritance Margin;Import Directives; IntelliSense - - Change completion list settings;Pre-select most recently used member - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Help > About diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.cs.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.cs.xlf index 81d31488dfb..5bd2eb5a8b0 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.cs.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.cs.xlf @@ -115,11 +115,6 @@ Používat rozšířené barvy;Barevné schéma editoru;Okraj dědičnosti;Direk IntelliSense - - Change completion list settings;Pre-select most recently used member - Změnit nastavení pro seznam dokončení;Předvolit naposledy použitou položku - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Komponenty Visual Basicu použité v IDE. V závislosti na typu vašeho projektu a nastavení se může použít jiná verze kompilátoru. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.de.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.de.xlf index a1d60e7b7b6..6f9cb6100b5 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.de.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.de.xlf @@ -115,11 +115,6 @@ Erweiterte Farben verwenden;Editor-Farbschema;Vererbungsspielraum;Richtlinien im IntelliSense - - Change completion list settings;Pre-select most recently used member - Einstellungen der Vervollständigungsliste ändern;Vorauswahl des zuletzt verwendeten Members - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Visual Basic-Komponenten, die in der IDE verwendet werden. Abhängig von Ihrem Projekttyp und den zugehörigen Einstellungen kann eine andere Version des Compilers verwendet werden. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.es.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.es.xlf index e44318db68f..8faf47620ae 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.es.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.es.xlf @@ -115,11 +115,6 @@ Usar colores mejorados;Combinación de colores del editor;Margen de herencia;Dir IntelliSense - - Change completion list settings;Pre-select most recently used member - Cambiar configuración de la lista de finalización;Preseleccionar el miembro usado recientemente - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Los componentes de Visual Basic utilizados en el IDE. En función del tipo de proyecto y la configuración, se podría utilizar una versión diferente del compilador. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.fr.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.fr.xlf index e6d8da6ba1a..572aa73c4da 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.fr.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.fr.xlf @@ -115,11 +115,6 @@ Utiliser des couleurs améliorées ; Modèle de couleurs de l’éditeur;Marge d IntelliSense - - Change completion list settings;Pre-select most recently used member - Modifier les paramètres de la liste de saisie semi-automatique ; Présélectionner le dernier membre utilisé - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Composants Visual Basic utilisés dans l'IDE. Selon votre type de projet et vos paramètres, une version différente du compilateur peut être utilisée. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.it.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.it.xlf index ce5d70f92be..791eabccf2d 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.it.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.it.xlf @@ -115,11 +115,6 @@ Usare colori avanzati; Combinazione colori editor;Margine di ereditarietà;Diret IntelliSense - - Change completion list settings;Pre-select most recently used member - Modifica impostazioni dell'elenco di completamento;Preseleziona membri usati di recente - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Componenti di Visual Basic usati nell'IDE. A seconda del tipo e delle impostazioni del processo, è possibile che venga usata una versione diversa del compilatore. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ja.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ja.xlf index 53a948f5134..2b66f456291 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ja.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ja.xlf @@ -115,11 +115,6 @@ JSON 文字列のエディター機能の検出と提供; IntelliSense - - Change completion list settings;Pre-select most recently used member - 入力候補一覧の設定を変更する;最近使用されたメンバーをあらかじめ選択する - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. IDE で使用する Visual Basic コンポーネント。プロジェクトの種類や設定に応じて、異なるバージョンのコンパイラを使用できます。 diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ko.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ko.xlf index 945d6f9153d..1ce61f86ece 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ko.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ko.xlf @@ -115,11 +115,6 @@ JSON 문자열 색상 지정, IntelliSense - - Change completion list settings;Pre-select most recently used member - 완성 목록 설정 변경;가장 최근에 사용한 멤버 미리 선택 - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. IDE에서 사용되는 Visual Basic 구성 요소입니다. 프로젝트 형식 및 설정에 따라 다른 버전의 컴파일러를 사용할 수 있습니다. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pl.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pl.xlf index c4c6b0e5a0a..12b9dfcbaad 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pl.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pl.xlf @@ -115,11 +115,6 @@ Używanie rozszerzonych kolorów; Schemat kolorów edytora;Margines dziedziczeni IntelliSense - - Change completion list settings;Pre-select most recently used member - Zmień ustawienia listy uzupełniania;Wybierz wstępnie ostatnio używaną składową - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Składniki języka Visual Basic używane w środowisku IDE. Zależnie od typu projektu i jego ustawień może być używana inna wersja kompilatora. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pt-BR.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pt-BR.xlf index e7eb504c535..36a51aa1d23 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pt-BR.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.pt-BR.xlf @@ -115,11 +115,6 @@ Usar cores aprimoradas;Esquema de Cores do Editor;Margem de Herança;Importar Di IntelliSense - - Change completion list settings;Pre-select most recently used member - Alterar as configurações de lista de conclusão;pre-selecionar membro usado recentemente - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Componentes do Visual Basic usados no IDE. Dependendo do seu tipo de projeto e configurações, uma versão diferente do compilador poderá ser usada. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ru.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ru.xlf index 3d7d4d69bc4..8d1daf3aee8 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ru.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.ru.xlf @@ -115,11 +115,6 @@ JSON; IntelliSense - - Change completion list settings;Pre-select most recently used member - Изменение параметров списка завершения; Предварительный выбор наиболее часто используемого члена - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. Компоненты Visual Basic, используемые в интегрированной среде разработки. В зависимости от типа и настроек проекта могут использоваться различные версии компилятора. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.tr.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.tr.xlf index 476f29fb3be..2629c9d3ac0 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.tr.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.tr.xlf @@ -115,11 +115,6 @@ Gelişmiş renkleri kullan;Düzenleyici Renk Düzeni;Devralma Kenar Boşluğu;İ IntelliSense - - Change completion list settings;Pre-select most recently used member - Tamamlanma listesi ayarlarını değiştir;en son kullanılan üyeyi önceden seç - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. IDE’de kullanılan Visual Basic bileşenleri. Projenizin türüne ve ayarlarına bağlı olarak, derleyicinin farklı bir sürümü kullanılabilir. diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hans.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hans.xlf index 48dd19bb6ec..ad0920fae9a 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hans.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hans.xlf @@ -115,11 +115,6 @@ JSON; IntelliSense - - Change completion list settings;Pre-select most recently used member - 更改完成列表设置;预先选择最近使用过的成员 - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. IDE 中使用的 Visual Basic 组件。可能使用其他版本的编译器,具体取决于你的项目类型和设置。 diff --git a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hant.xlf b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hant.xlf index cef765e0629..536a1792cc0 100644 --- a/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hant.xlf +++ b/src/roslyn/src/VisualStudio/VisualBasic/Impl/xlf/VSPackage.zh-Hant.xlf @@ -115,11 +115,6 @@ JSON; IntelliSense - - Change completion list settings;Pre-select most recently used member - 變更完成清單設定;預先選取最近使用的成員 - IntelliSense options page keywords - Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used. 在 IDE 中使用的 Visual Basic 元件。根據您的專案類型與設定,可能會使用其他版本的編譯器。 diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs index db708dc9dfb..7dc2b9df9be 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Guids.cs @@ -16,5 +16,5 @@ internal static class GuidList public const string guidVisualStudioDiagnosticsWindowCmdSetString = "f22c2499-790a-4b6c-b0fd-b6f0491e1c9c"; public const string guidToolWindowPersistanceString = "b2da68d7-fd1c-491a-a9a0-24f597b9f56c"; - public static readonly Guid guidVisualStudioDiagnosticsWindowCmdSet = new Guid(guidVisualStudioDiagnosticsWindowCmdSetString); + public static readonly Guid guidVisualStudioDiagnosticsWindowCmdSet = new(guidVisualStudioDiagnosticsWindowCmdSetString); }; diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs index 59a0f221710..58eb016c5a3 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/Loggers/OutputWindowLogger.cs @@ -48,9 +48,9 @@ public void LogBlockEnd(FunctionId functionId, LogMessage logMessage, int unique private sealed class OutputPane { - private static readonly Guid s_outputPaneGuid = new Guid("BBAFF416-4AF5-41F2-9F93-91F283E43C3B"); + private static readonly Guid s_outputPaneGuid = new("BBAFF416-4AF5-41F2-9F93-91F283E43C3B"); - public static readonly OutputPane s_instance = new OutputPane(); + public static readonly OutputPane s_instance = new(); private readonly IServiceProvider _serviceProvider; private readonly IThreadingContext _threadingContext; diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs index 12c6633d20f..e38d53f5a07 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs @@ -58,7 +58,7 @@ private sealed class MemoryHogger private const int MonitorDelay = 10000; // 10 seconds private readonly List _blocks = []; - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _cancellationTokenSource = new(); public MemoryHogger() { diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs index 960d1d28892..f0250e87f77 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/PerfMargin/StatusIndicator.xaml.cs @@ -45,8 +45,8 @@ private void ActivityLevel_IsActiveChanged(object sender, EventArgs e) } private const double MinimumScale = 0.2; - private static readonly DoubleAnimation s_growAnimation = new DoubleAnimation(1.0, new Duration(TimeSpan.FromSeconds(1.0)), FillBehavior.HoldEnd); - private static readonly DoubleAnimation s_shrinkAnimation = new DoubleAnimation(0.0, new Duration(TimeSpan.FromSeconds(0.33333)), FillBehavior.HoldEnd); + private static readonly DoubleAnimation s_growAnimation = new(1.0, new Duration(TimeSpan.FromSeconds(1.0)), FillBehavior.HoldEnd); + private static readonly DoubleAnimation s_shrinkAnimation = new(0.0, new Duration(TimeSpan.FromSeconds(0.33333)), FillBehavior.HoldEnd); public void UpdateOnUIThread() { diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs index 76b4878bfed..09014a9d240 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/ProjectionSpanTag.cs @@ -12,7 +12,7 @@ internal sealed class ProjectionSpanTag : TextMarkerTag { public const string TagId = "ProjectionTag"; - public static readonly ProjectionSpanTag Instance = new ProjectionSpanTag(); + public static readonly ProjectionSpanTag Instance = new(); public ProjectionSpanTag() : base(TagId) diff --git a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs index 3a208ba5c58..240b4375c65 100644 --- a/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs +++ b/src/roslyn/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VenusMargin/VenusMargin.cs @@ -16,7 +16,7 @@ internal sealed class VenusMargin : IWpfTextViewMargin public const string MarginName = "VenusMargin"; private readonly IProjectionBuffer _projectionBuffer; - private readonly ProjectionBufferViewModel _viewModel = new ProjectionBufferViewModel(); + private readonly ProjectionBufferViewModel _viewModel = new(); private readonly ProjectionBufferMargin _control; private bool _isDisposed = false; diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs index 3222e5fd697..42799084a78 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs @@ -31,7 +31,7 @@ internal sealed class CompletionHandler : ILspServiceDocumentRequestHandler request.TextDocument; protected override VSInternalDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) - => new VSInternalDiagnosticReport { Diagnostics = diagnostics, ResultId = resultId }; + => new() { Diagnostics = diagnostics, ResultId = resultId }; protected override ImmutableArray GetDocuments(RequestContext context) { diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index f96133e81b6..234e9f33784 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -29,7 +29,7 @@ public WorkspacePullDiagnosticHandler( { } protected override VSInternalWorkspaceDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) - => new VSInternalWorkspaceDiagnosticReport { TextDocument = identifier, Diagnostics = diagnostics, ResultId = resultId }; + => new() { TextDocument = identifier, Diagnostics = diagnostics, ResultId = resultId }; /// /// Collect all the opened documents from solution. diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs index a8f3afa25c7..c56e33016a2 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlProjectService.cs @@ -35,7 +35,7 @@ internal sealed partial class XamlProjectService : IDisposable private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory; private readonly IThreadingContext _threadingContext; private readonly Dictionary _xamlProjects = []; - private readonly ConcurrentDictionary _documentIds = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _documentIds = new(StringComparer.OrdinalIgnoreCase); private readonly WorkspaceEventRegistration _documentClosedHandlerDisposer; private RunningDocumentTable? _rdt; diff --git a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs index c5881cb3233..d7fe48edfb0 100644 --- a/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs +++ b/src/roslyn/src/VisualStudio/Xaml/Impl/Implementation/XamlTextViewCreationListener.cs @@ -23,7 +23,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml; internal sealed partial class XamlTextViewCreationListener : IWpfTextViewCreationListener { // Temporary UIConext GUID owned by the XAML language service until we can get a KnownUIContext - private static readonly Guid s_serverUIContextGuid = new Guid("39F55746-6E65-4FCF-BEC5-EC0B466EAC0F"); + private static readonly Guid s_serverUIContextGuid = new("39F55746-6E65-4FCF-BEC5-EC0B466EAC0F"); private readonly IServiceProvider _serviceProvider; private readonly XamlProjectService _projectService; diff --git a/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 7ad89b49d26..ab0a54704fb 100644 --- a/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/roslyn/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -258,7 +258,6 @@ private static string StripExplicitInterfaceName(string name) public override SyntaxNode OperatorDeclaration(OperatorKind kind, IEnumerable? parameters = null, SyntaxNode? returnType = null, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable? statements = null) { return OperatorDeclaration(GetOperatorName(kind), isImplicitConversion: kind == OperatorKind.ImplicitConversion, parameters, returnType, accessibility, modifiers, statements); - } private protected override SyntaxNode OperatorDeclaration(string operatorName, bool isImplicitConversion, IEnumerable? parameters = null, SyntaxNode? returnType = null, Accessibility accessibility = Accessibility.NotApplicable, DeclarationModifiers modifiers = default, IEnumerable? statements = null) @@ -3235,10 +3234,10 @@ internal override bool IsRegularOrDocComment(SyntaxTrivia trivia) #region Statements and Expressions public override SyntaxNode AddEventHandler(SyntaxNode @event, SyntaxNode handler) - => SyntaxFactory.AssignmentExpression(SyntaxKind.AddAssignmentExpression, (ExpressionSyntax)@event, (ExpressionSyntax)Parenthesize(handler)); + => SyntaxFactory.AssignmentExpression(SyntaxKind.AddAssignmentExpression, (ExpressionSyntax)@event, (ExpressionSyntax)ParenthesizeNonSimple(handler)); public override SyntaxNode RemoveEventHandler(SyntaxNode @event, SyntaxNode handler) - => SyntaxFactory.AssignmentExpression(SyntaxKind.SubtractAssignmentExpression, (ExpressionSyntax)@event, (ExpressionSyntax)Parenthesize(handler)); + => SyntaxFactory.AssignmentExpression(SyntaxKind.SubtractAssignmentExpression, (ExpressionSyntax)@event, (ExpressionSyntax)ParenthesizeNonSimple(handler)); public override SyntaxNode AwaitExpression(SyntaxNode expression) => SyntaxFactory.AwaitExpression((ExpressionSyntax)expression); @@ -3364,20 +3363,20 @@ public override SyntaxNode ElementAccessExpression(SyntaxNode expression, IEnume internal override SyntaxToken NumericLiteralToken(string text, ulong value) => SyntaxFactory.Literal(text, value); - private static SyntaxNode Parenthesize(SyntaxNode expression, bool includeElasticTrivia = true, bool addSimplifierAnnotation = true) - => CSharpSyntaxGeneratorInternal.Parenthesize(expression, includeElasticTrivia, addSimplifierAnnotation); + private static SyntaxNode ParenthesizeNonSimple(SyntaxNode expression) + => CSharpSyntaxGeneratorInternal.ParenthesizeNonSimple(expression); public override SyntaxNode IsTypeExpression(SyntaxNode expression, SyntaxNode type) - => SyntaxFactory.BinaryExpression(SyntaxKind.IsExpression, (ExpressionSyntax)Parenthesize(expression), (TypeSyntax)type); + => SyntaxFactory.BinaryExpression(SyntaxKind.IsExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression), (TypeSyntax)type); public override SyntaxNode TypeOfExpression(SyntaxNode type) => SyntaxFactory.TypeOfExpression((TypeSyntax)type); public override SyntaxNode TryCastExpression(SyntaxNode expression, SyntaxNode type) - => SyntaxFactory.BinaryExpression(SyntaxKind.AsExpression, (ExpressionSyntax)Parenthesize(expression), (TypeSyntax)type); + => SyntaxFactory.BinaryExpression(SyntaxKind.AsExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression), (TypeSyntax)type); public override SyntaxNode AssignmentStatement(SyntaxNode left, SyntaxNode right) - => SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, (ExpressionSyntax)left, (ExpressionSyntax)Parenthesize(right)); + => SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, (ExpressionSyntax)left, (ExpressionSyntax)ParenthesizeNonSimple(right)); private static SyntaxNode CreateBinaryExpression(SyntaxKind syntaxKind, SyntaxNode left, SyntaxNode right) => CSharpSyntaxGeneratorInternal.CreateBinaryExpression(syntaxKind, left, right); @@ -3407,7 +3406,7 @@ public override SyntaxNode GreaterThanOrEqualExpression(SyntaxNode left, SyntaxN => CreateBinaryExpression(SyntaxKind.GreaterThanOrEqualExpression, left, right); public override SyntaxNode NegateExpression(SyntaxNode expression) - => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.UnaryMinusExpression, (ExpressionSyntax)Parenthesize(expression)); + => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.UnaryMinusExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression)); public override SyntaxNode AddExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.AddExpression, left, right); @@ -3428,7 +3427,7 @@ public override SyntaxNode BitwiseAndExpression(SyntaxNode left, SyntaxNode righ => CreateBinaryExpression(SyntaxKind.BitwiseAndExpression, left, right); public override SyntaxNode BitwiseNotExpression(SyntaxNode operand) - => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.BitwiseNotExpression, (ExpressionSyntax)Parenthesize(operand)); + => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.BitwiseNotExpression, (ExpressionSyntax)ParenthesizeNonSimple(operand)); public override SyntaxNode LogicalAndExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.LogicalAndExpression, left, right); @@ -3437,10 +3436,10 @@ public override SyntaxNode LogicalOrExpression(SyntaxNode left, SyntaxNode right => CreateBinaryExpression(SyntaxKind.LogicalOrExpression, left, right); public override SyntaxNode LogicalNotExpression(SyntaxNode expression) - => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, (ExpressionSyntax)Parenthesize(expression)); + => SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, (ExpressionSyntax)ParenthesizeNonSimple(expression)); public override SyntaxNode ConditionalExpression(SyntaxNode condition, SyntaxNode whenTrue, SyntaxNode whenFalse) - => SyntaxFactory.ConditionalExpression((ExpressionSyntax)Parenthesize(condition), (ExpressionSyntax)Parenthesize(whenTrue), (ExpressionSyntax)Parenthesize(whenFalse)); + => SyntaxFactory.ConditionalExpression((ExpressionSyntax)ParenthesizeNonSimple(condition), (ExpressionSyntax)ParenthesizeNonSimple(whenTrue), (ExpressionSyntax)ParenthesizeNonSimple(whenFalse)); public override SyntaxNode CoalesceExpression(SyntaxNode left, SyntaxNode right) => CreateBinaryExpression(SyntaxKind.CoalesceExpression, left, right); @@ -3739,5 +3738,18 @@ internal override SyntaxNode ParseExpression(string stringToParse) internal override SyntaxNode ParseTypeName(string stringToParse) => SyntaxFactory.ParseTypeName(stringToParse); + internal override SyntaxNode ExtensionBlockDeclaration( + SyntaxNode extensionParameter, + IEnumerable? typeParameters, + IEnumerable members) + { + SyntaxList extensionMembers = [.. members.OfType().WhereNotNull()]; + var typeParameterList = AsTypeParameterList(typeParameters); + + return SyntaxFactory.ExtensionBlockDeclaration(attributeLists: default, modifiers: default, ExtensionKeyword, + typeParameterList, parameterList: AsParameterList([extensionParameter]), + constraintClauses: default, OpenBraceToken, extensionMembers, CloseBraceToken, default); + } + #endregion } diff --git a/src/roslyn/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs b/src/roslyn/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs index 38065e9be87..8d933cd41d7 100644 --- a/src/roslyn/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs +++ b/src/roslyn/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs @@ -39,10 +39,8 @@ private SyntaxNode ProcessTypeSyntax(TypeSyntax typeSyntax) var typeStyle = CSharpUseImplicitTypeHelper.Instance.AnalyzeTypeName( typeSyntax, this.SemanticModel, this.Options, this.CancellationToken); - if (!typeStyle.IsStylePreferred || !typeStyle.CanConvert()) - { + if (!typeStyle.IsStylePreferred || !typeStyle.CanConvert) return typeSyntax; - } return SyntaxFactory.IdentifierName("var") .WithLeadingTrivia(typeSyntax.GetLeadingTrivia()) diff --git a/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs b/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs index d1c8049b0c8..4b068b74a58 100644 --- a/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs +++ b/src/roslyn/src/Workspaces/CSharpTest/CSharpCommandLineParserServiceTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests; public sealed class CSharpCommandLineParserServiceTests { private static readonly string s_directory = Path.GetTempPath(); - private readonly CSharpCommandLineParserService _parser = new CSharpCommandLineParserService(); + private readonly CSharpCommandLineParserService _parser = new(); private CSharpCommandLineArguments GetArguments(params string[] args) => (CSharpCommandLineArguments)_parser.Parse(args, baseDirectory: s_directory, isInteractive: false, sdkDirectory: s_directory); diff --git a/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs index d064441285f..72b63dad2b1 100644 --- a/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs +++ b/src/roslyn/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs @@ -344,43 +344,43 @@ public void TestSymbolTypeExpressions() [Fact] public void TestMathAndLogicExpressions() { - VerifySyntax(Generator.NegateExpression(Generator.IdentifierName("x")), "-(x)"); - VerifySyntax(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) + (y)"); - VerifySyntax(Generator.SubtractExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) - (y)"); - VerifySyntax(Generator.MultiplyExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) * (y)"); - VerifySyntax(Generator.DivideExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) / (y)"); - VerifySyntax(Generator.ModuloExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) % (y)"); + VerifySyntax(Generator.NegateExpression(Generator.IdentifierName("x")), "-x"); + VerifySyntax(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x + y"); + VerifySyntax(Generator.SubtractExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x - y"); + VerifySyntax(Generator.MultiplyExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x * y"); + VerifySyntax(Generator.DivideExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x / y"); + VerifySyntax(Generator.ModuloExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x % y"); - VerifySyntax(Generator.BitwiseNotExpression(Generator.IdentifierName("x")), "~(x)"); - VerifySyntax(Generator.BitwiseAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) & (y)"); - VerifySyntax(Generator.BitwiseOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) | (y)"); + VerifySyntax(Generator.BitwiseNotExpression(Generator.IdentifierName("x")), "~x"); + VerifySyntax(Generator.BitwiseAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x & y"); + VerifySyntax(Generator.BitwiseOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x | y"); - VerifySyntax(Generator.LogicalNotExpression(Generator.IdentifierName("x")), "!(x)"); - VerifySyntax(Generator.LogicalAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) && (y)"); - VerifySyntax(Generator.LogicalOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) || (y)"); + VerifySyntax(Generator.LogicalNotExpression(Generator.IdentifierName("x")), "!x"); + VerifySyntax(Generator.LogicalAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x && y"); + VerifySyntax(Generator.LogicalOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x || y"); } [Fact] public void TestEqualityAndInequalityExpressions() { - VerifySyntax(Generator.ReferenceEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) == (y)"); - VerifySyntax(Generator.ValueEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) == (y)"); + VerifySyntax(Generator.ReferenceEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x == y"); + VerifySyntax(Generator.ValueEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x == y"); - VerifySyntax(Generator.ReferenceNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) != (y)"); - VerifySyntax(Generator.ValueNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) != (y)"); + VerifySyntax(Generator.ReferenceNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x != y"); + VerifySyntax(Generator.ValueNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x != y"); - VerifySyntax(Generator.LessThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) < (y)"); - VerifySyntax(Generator.LessThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) <= (y)"); + VerifySyntax(Generator.LessThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x < y"); + VerifySyntax(Generator.LessThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x <= y"); - VerifySyntax(Generator.GreaterThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) > (y)"); - VerifySyntax(Generator.GreaterThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) >= (y)"); + VerifySyntax(Generator.GreaterThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x > y"); + VerifySyntax(Generator.GreaterThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x >= y"); } [Fact] public void TestConditionalExpressions() { - VerifySyntax(Generator.CoalesceExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) ?? (y)"); - VerifySyntax(Generator.ConditionalExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"), Generator.IdentifierName("z")), "(x) ? (y) : (z)"); + VerifySyntax(Generator.CoalesceExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x ?? y"); + VerifySyntax(Generator.ConditionalExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"), Generator.IdentifierName("z")), "x ? y : z"); } [Fact] @@ -391,8 +391,8 @@ public void TestMemberAccessExpressions() VerifySyntax(Generator.MemberAccessExpression(Generator.MemberAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "x.y.z"); VerifySyntax(Generator.MemberAccessExpression(Generator.InvocationExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "x(y).z"); VerifySyntax(Generator.MemberAccessExpression(Generator.ElementAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "x[y].z"); - VerifySyntax(Generator.MemberAccessExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "((x) + (y)).z"); - VerifySyntax(Generator.MemberAccessExpression(Generator.NegateExpression(Generator.IdentifierName("x")), Generator.IdentifierName("y")), "(-(x)).y"); + VerifySyntax(Generator.MemberAccessExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "(x + y).z"); + VerifySyntax(Generator.MemberAccessExpression(Generator.NegateExpression(Generator.IdentifierName("x")), Generator.IdentifierName("y")), "(-x).y"); } [Fact] @@ -458,21 +458,21 @@ public void TestElementAccessExpressions() VerifySyntax( Generator.ElementAccessExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), - "((x) + (y))[z]"); + "(x + y)[z]"); } [Fact] public void TestCastAndConvertExpressions() { - VerifySyntax(Generator.CastExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x)(y)"); - VerifySyntax(Generator.ConvertExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x)(y)"); + VerifySyntax(Generator.CastExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x)y"); + VerifySyntax(Generator.ConvertExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x)y"); } [Fact] public void TestIsAndAsExpressions() { - VerifySyntax(Generator.IsTypeExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) is y"); - VerifySyntax(Generator.TryCastExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) as y"); + VerifySyntax(Generator.IsTypeExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x is y"); + VerifySyntax(Generator.TryCastExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x as y"); VerifySyntax(Generator.TypeOfExpression(Generator.IdentifierName("x")), "typeof(x)"); } @@ -493,12 +493,12 @@ public void TestInvocationExpressions() VerifySyntax(Generator.InvocationExpression(Generator.MemberAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "x.y()"); VerifySyntax(Generator.InvocationExpression(Generator.ElementAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "x[y]()"); VerifySyntax(Generator.InvocationExpression(Generator.InvocationExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "x(y)()"); - VerifySyntax(Generator.InvocationExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "((x) + (y))()"); + VerifySyntax(Generator.InvocationExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "(x + y)()"); } [Fact] public void TestAssignmentStatement() - => VerifySyntax(Generator.AssignmentStatement(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x = (y)"); + => VerifySyntax(Generator.AssignmentStatement(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x = y"); [Fact] public void TestExpressionStatement() @@ -523,13 +523,13 @@ public void TestLocalDeclarationStatements() public void TestAddHandlerExpressions() => VerifySyntax( Generator.AddEventHandler(Generator.IdentifierName("@event"), Generator.IdentifierName("handler")), - "@event += (handler)"); + "@event += handler"); [Fact] public void TestSubtractHandlerExpressions() => VerifySyntax( Generator.RemoveEventHandler(Generator.IdentifierName("@event"), - Generator.IdentifierName("handler")), "@event -= (handler)"); + Generator.IdentifierName("handler")), "@event -= handler"); [Fact] public void TestAwaitExpressions() @@ -5288,6 +5288,387 @@ public class C """); } + [Fact] + public void TestExtensionDeclaration_01() + { + var compilation = Compile(""" + public static class E + { + extension(int i) + { + public void M() + { + } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::System.Int32 i) + { + public void M() + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_02() + { + // unnamed extension parameter + var compilation = Compile(""" + public static class E + { + extension(int) + { + public static int P { get => 0; set { } } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::System.Int32) + { + public static global::System.Int32 P { get; set; } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_03() + { + // generic + var compilation = Compile(""" + public static class E + { + extension(int) + { + public static int P => 0; + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::System.Int32) + { + public static global::System.Int32 P { get; } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_04() + { + // null extension parameter + var compilation = Compile(""" + public static class E + { + extension(__arglist) + { + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + Assert.Throws(() => Generator.Declaration(symbol)); + } + + [Fact] + public void TestExtensionDeclaration_05() + { + // operator + var compilation = Compile(""" + public static class E + { + extension(C) + { + public static C operator +(C c1, C c2) => null; + } + } + + class C { } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public static class E : global::System.Object + { + extension(global::C) + { + public static global::C operator +(global::C c1, global::C c2) + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_06() + { + // in struct + var compilation = Compile(""" + public struct E + { + extension(int) + { + public void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public struct E + { + extension(global::System.Int32) + { + public void M() + { + } + } + + public E() + { + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_07() + { + // in non-static class + var compilation = Compile(""" + public class E + { + extension(int) + { + public void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + public class E : global::System.Object + { + extension(global::System.Int32) + { + public void M() + { + } + } + + public E() + { + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_08() + { + // top-level + var compilation = Compile(""" + extension(int) + { + public void M() { } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + extension(global::System.Int32) + { + public void M() + { + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_09() + { + // nested in type + var compilation = Compile(""" + static class E1 + { + static class E2 + { + extension(int) + { + public void M() { } + } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E1").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E1 : global::System.Object + { + private static class E2 : global::System.Object + { + extension(global::System.Int32) + { + public void M() + { + } + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_10() + { + // merged extension blocks + var compilation = Compile(""" + static class E + { + extension(int) + { + public void M1() { } + } + extension(int) + { + public void M2() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E : global::System.Object + { + extension(global::System.Int32) + { + public void M1() + { + } + } + + extension(global::System.Int32) + { + public void M2() + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_11() + { + // type parameter constraints + var compilation = Compile(""" + static class E + { + extension(int) where T : class + { + public void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E : global::System.Object + { + extension(global::System.Int32) + where T : class + { + public void M() + { + } + } + } + """); + } + + [Fact] + public void TestExtensionDeclaration_12() + { + // static extension method + var compilation = Compile(""" + static class E + { + extension(int) + { + public static void M() { } + } + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("E").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal static class E : global::System.Object + { + extension(global::System.Int32) + { + public static void M() + { + } + } + } + """); + } + + [Fact] + public void Operator_01() + { + var compilation = Compile(""" + class C + { + public static C operator+(C c1, C c2) => throw null; + } + """); + + var symbol = compilation.GlobalNamespace.GetTypeMembers("C").Single(); + VerifySyntax(Generator.Declaration(symbol), + """ + internal class C : global::System.Object + { + public static global::C operator +(global::C c1, global::C c2) + { + } + + public C() + { + } + } + """); + } + private static void TestModifiersAsync(DeclarationModifiers modifiers, string markup) { MarkupTestFile.GetSpan(markup, out var code, out var span); diff --git a/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs b/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs index e48e2eec2de..c098a0aa5c0 100644 --- a/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs +++ b/src/roslyn/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs @@ -106,7 +106,7 @@ public IEnumerable> GetExports() } internal TestAccessor GetTestAccessor() - => new TestAccessor(this); + => new(this); private readonly struct ExportKey : IEquatable { diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs index f7b15e6fe3e..15a3d41632b 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFix.cs @@ -2,12 +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 System.Diagnostics; -using System.Linq; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CodeFixes; @@ -18,60 +14,19 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// internal sealed class CodeFix { - public readonly Project Project; public readonly CodeAction Action; - public readonly ImmutableArray Diagnostics; /// - /// This is the diagnostic that will show up in the preview pane header when a particular fix is selected in the - /// light bulb menu. We also group all fixes with the same together (into a single - /// SuggestedActionSet) in the light bulb menu. + /// Note: a code fix can fix one or more diagnostics. For the purposes of display in a UI, it is recommended that + /// the first diagnostic in this list be treated as the "primary" diagnostic. For example, withing Visual Studio, + /// all fixes with the same primary diagnostic are grouped together in the light bulb menu. /// - /// - /// A given fix can fix one or more diagnostics. However, our light bulb UI (preview pane, grouping of fixes in the - /// light bulb menu etc.) currently keeps things simple and pretends that each fix fixes a single . - /// - /// Implementation-wise the is always the first diagnostic that the supplied when registering the fix (). This could change in the future, - /// if we decide to change the UI to depict the true mapping between fixes and diagnostics or if we decide to use - /// some other heuristic to determine the . - /// - public Diagnostic PrimaryDiagnostic => Diagnostics[0]; - - public CodeFix(Project project, CodeAction action, Diagnostic diagnostic) - { - Project = project; - Action = action; - Diagnostics = [diagnostic]; - } + public readonly ImmutableArray Diagnostics; - public CodeFix(Project project, CodeAction action, ImmutableArray diagnostics) + public CodeFix(CodeAction action, ImmutableArray diagnostics) { - Debug.Assert(!diagnostics.IsDefaultOrEmpty); - Project = project; + Contract.ThrowIfTrue(diagnostics.IsDefaultOrEmpty); Action = action; Diagnostics = diagnostics; } - - public DiagnosticData GetPrimaryDiagnosticData() - { - var diagnostic = PrimaryDiagnostic; - - if (diagnostic.Location.IsInSource) - { - var document = Project.GetDocument(diagnostic.Location.SourceTree); - if (document != null) - return DiagnosticData.Create(diagnostic, document); - } - else if (diagnostic.Location.Kind == LocationKind.ExternalFile) - { - var document = Project.Documents.FirstOrDefault(d => d.FilePath == diagnostic.Location.GetLineSpan().Path); - if (document != null) - return DiagnosticData.Create(diagnostic, document); - } - - return DiagnosticData.Create(diagnostic, Project); - } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs index b5d436d2d64..ac09a56a53c 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/CodeFixProvider.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; namespace Microsoft.CodeAnalysis.CodeFixes; @@ -13,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// Implement this type to provide fixes for source code problems. /// Remember to use so the host environment can offer your fixes in a UI. /// -public abstract class CodeFixProvider +public abstract class CodeFixProvider : IRefactorOrFixProvider { private protected ImmutableArray CustomTags = []; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs index 796d9d7f1ce..2373f19e382 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DocumentBasedFixAllProvider.cs +++ b/src/roslyn/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.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs index a450cde44c7..a42d6ac9119 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// /// Context for "Fix all occurrences" code fixes provided by a . /// -public partial class FixAllContext : IFixAllContext +public partial class FixAllContext : IRefactorOrFixAllContext { internal FixAllState State { get; } @@ -71,14 +71,14 @@ public partial class FixAllContext : IFixAllContext public IProgress Progress { get; } #region IFixAllContext implementation - IFixAllState IFixAllContext.State => this.State; + IRefactorOrFixAllState IRefactorOrFixAllContext.State => this.State; - object IFixAllContext.Provider => this.CodeFixProvider; + IRefactorOrFixProvider IRefactorOrFixAllContext.Provider => this.CodeFixProvider; - string IFixAllContext.GetDefaultFixAllTitle() + string IRefactorOrFixAllContext.GetDefaultTitle() => this.GetDefaultFixAllTitle(); - IFixAllContext IFixAllContext.With( + IRefactorOrFixAllContext IRefactorOrFixAllContext.With( Optional<(Document? document, Project project)> documentAndProject, Optional scope, Optional codeActionEquivalenceKey, diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs index b5aac3e0083..eb47a4f119f 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllProvider.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes; /// Implement this abstract type to provide fix all/multiple occurrences code fixes for source code problems. /// Alternatively, you can use any of the well known fix all providers from . /// -public abstract class FixAllProvider : IFixAllProvider +public abstract class FixAllProvider : IRefactorOrFixAllProvider { private protected static ImmutableArray DefaultSupportedFixAllScopes = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution]; @@ -24,7 +24,7 @@ private protected static ImmutableArray DefaultSupportedFixAllScope public virtual IEnumerable GetSupportedFixAllScopes() => DefaultSupportedFixAllScopes; - CodeActionCleanup IFixAllProvider.Cleanup => this.Cleanup; + CodeActionCleanup IRefactorOrFixAllProvider.Cleanup => this.Cleanup; internal virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; @@ -96,7 +96,7 @@ internal static FixAllProvider Create( } #region IFixAllProvider implementation - Task IFixAllProvider.GetFixAsync(IFixAllContext fixAllContext) + Task IRefactorOrFixAllProvider.GetCodeActionAsync(IRefactorOrFixAllContext fixAllContext) => this.GetFixAsync((FixAllContext)fixAllContext); #endregion diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs index 7685dda9619..83c8a601567 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllScope.cs @@ -10,22 +10,22 @@ namespace Microsoft.CodeAnalysis.CodeFixes; public enum FixAllScope { /// - /// Scope to fix all occurences of diagnostic(s) in the entire document. + /// Scope to fix all occurrences of diagnostic(s) in the entire document. /// Document, /// - /// Scope to fix all occurences of diagnostic(s) in the entire project. + /// Scope to fix all occurrences of diagnostic(s) in the entire project. /// Project, /// - /// Scope to fix all occurences of diagnostic(s) in the entire solution. + /// Scope to fix all occurrences of diagnostic(s) in the entire solution. /// Solution, /// - /// Custom scope to fix all occurences of diagnostic(s). This scope can + /// Custom scope to fix all occurrences of diagnostic(s). This scope can /// be used by custom s and custom code fix engines. /// Custom, diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs index 8bc5930cccc..715fcb805a0 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs @@ -3,13 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Internal.Log; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -internal abstract partial class CommonFixAllState : IFixAllState - where TFixAllProvider : IFixAllProvider +internal abstract partial class CommonFixAllState + : IRefactorOrFixAllState + where TProvider : IRefactorOrFixProvider + where TFixAllProvider : IRefactorOrFixAllProvider where TFixAllState : CommonFixAllState { public int CorrelationId { get; } = CorrelationIdFactory.GetNextId(); @@ -63,11 +65,11 @@ public TFixAllState With( } #region IFixAllState implementation - IFixAllProvider IFixAllState.FixAllProvider => this.FixAllProvider!; + IRefactorOrFixAllProvider IRefactorOrFixAllState.FixAllProvider => this.FixAllProvider; - object IFixAllState.Provider => this.Provider!; + IRefactorOrFixProvider IRefactorOrFixAllState.Provider => this.Provider; - IFixAllState IFixAllState.With( + IRefactorOrFixAllState IRefactorOrFixAllState.With( Optional<(Document? document, Project project)> documentAndProject, Optional scope, Optional codeActionEquivalenceKey) diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs index 9e2cd45ee9c..bd3b8999969 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DefaultFixAllProviderHelpers.cs @@ -24,7 +24,7 @@ internal static class DefaultFixAllProviderHelpers string title, TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext { // We're about to do a lot of computation to compute all the diagnostics needed and to perform the // changes. Keep this solution alive on the OOP side so that we never drop it and then resync it @@ -50,19 +50,19 @@ FixAllScope.Document or FixAllScope.ContainingMember or FixAllScope.ContainingTy private static Task GetDocumentFixesAsync( TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext => fixAllContextsAsync(fixAllContext, [fixAllContext]); private static Task GetProjectFixesAsync( TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext => fixAllContextsAsync(fixAllContext, [(TFixAllContext)fixAllContext.With((document: null, fixAllContext.State.Project))]); private static Task GetSolutionFixesAsync( TFixAllContext fixAllContext, Func, Task> fixAllContextsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext { var solution = fixAllContext.State.Solution; var dependencyGraph = solution.GetProjectDependencyGraph(); diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs index dbeca1f4975..a159f7a0838 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs @@ -26,7 +26,7 @@ internal static class DocumentBasedFixAllProviderHelpers IProgress progressTracker, string progressTrackerDescription, Func, Task> getFixedDocumentsAsync) - where TFixAllContext : IFixAllContext + where TFixAllContext : IRefactorOrFixAllContext { var cancellationToken = originalFixAllContext.CancellationToken; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs index b0f45df201e..2c30c8b9329 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllKind.cs @@ -5,7 +5,7 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// An enum to distinguish if we are performing a Fix all occurences for a code fix or a code refactoring. +/// An enum to distinguish if we are performing a Fix all occurrences for a code fix or a code refactoring. /// internal enum FixAllKind { diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs index 3cb0a3e384c..9a2460f00fc 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs @@ -38,7 +38,7 @@ internal static class FixAllLogger private const string TotalDiagnosticsToFix = nameof(TotalDiagnosticsToFix); private const string TotalFixesToMerge = nameof(TotalFixesToMerge); - public static void LogState(IFixAllState fixAllState, bool isInternalProvider) + public static void LogState(IRefactorOrFixAllState fixAllState, bool isInternalProvider) { FunctionId functionId; string providerKey; diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs index 0029db84f0f..66bdccd8d01 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllProviderInfo.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 Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; @@ -16,11 +15,11 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// internal abstract class FixAllProviderInfo { - public readonly IFixAllProvider FixAllProvider; + public readonly IRefactorOrFixAllProvider FixAllProvider; public readonly ImmutableArray SupportedScopes; private FixAllProviderInfo( - IFixAllProvider fixAllProvider, + IRefactorOrFixAllProvider fixAllProvider, ImmutableArray supportedScopes) { FixAllProvider = fixAllProvider; @@ -69,19 +68,15 @@ private FixAllProviderInfo( /// private static FixAllProviderInfo? CreateWithCodeRefactoring(CodeRefactoringProvider provider) { - var fixAllProvider = provider.GetFixAllProvider(); - if (fixAllProvider == null) - { + var refactorAllProvider = provider.GetRefactorAllProvider(); + if (refactorAllProvider == null) return null; - } - var scopes = fixAllProvider.GetSupportedFixAllScopes().ToImmutableArrayOrEmpty(); + var scopes = refactorAllProvider.GetSupportedRefactorAllScopes().ToImmutableArrayOrEmpty(); if (scopes.IsEmpty) - { return null; - } - return new CodeRefactoringFixAllProviderInfo(fixAllProvider, scopes); + return new CodeRefactoringFixAllProviderInfo(refactorAllProvider, scopes); } /// @@ -91,15 +86,11 @@ private FixAllProviderInfo( { var fixAllProvider = provider.GetFixAllProvider(); if (fixAllProvider == null) - { return null; - } var scopes = fixAllProvider.GetSupportedFixAllScopes().ToImmutableArrayOrEmpty(); if (scopes.IsEmpty) - { return null; - } return new SuppressionFixerFixAllProviderInfo(fixAllProvider, provider, scopes); } @@ -107,7 +98,7 @@ private FixAllProviderInfo( public abstract bool CanBeFixed(Diagnostic diagnostic); private sealed class CodeFixerFixAllProviderInfo( - IFixAllProvider fixAllProvider, + IRefactorOrFixAllProvider fixAllProvider, ImmutableArray supportedDiagnosticIds, ImmutableArray supportedScopes) : FixAllProviderInfo(fixAllProvider, supportedScopes) { @@ -116,9 +107,10 @@ public override bool CanBeFixed(Diagnostic diagnostic) } private sealed class SuppressionFixerFixAllProviderInfo( - IFixAllProvider fixAllProvider, + IRefactorOrFixAllProvider fixAllProvider, IConfigurationFixProvider suppressionFixer, - ImmutableArray supportedScopes) : FixAllProviderInfo(fixAllProvider, supportedScopes) + ImmutableArray supportedScopes) + : FixAllProviderInfo(fixAllProvider, supportedScopes) { private readonly Func _canBeSuppressedOrUnsuppressed = suppressionFixer.IsFixableDiagnostic; @@ -127,8 +119,9 @@ public override bool CanBeFixed(Diagnostic diagnostic) } private sealed class CodeRefactoringFixAllProviderInfo( - IFixAllProvider fixAllProvider, - ImmutableArray supportedScopes) : FixAllProviderInfo(fixAllProvider, supportedScopes) + IRefactorOrFixAllProvider fixAllProvider, + ImmutableArray supportedScopes) + : FixAllProviderInfo(fixAllProvider, supportedScopes.SelectAsArray(s => s.ToFixAllScope())) { public override bool CanBeFixed(Diagnostic diagnostic) => throw ExceptionUtilities.Unreachable(); diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllContext.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllContext.cs similarity index 69% rename from src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllContext.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllContext.cs index e4a930af403..a14de03aff8 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllContext.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllContext.cs @@ -5,21 +5,22 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Represents a FixAllContext for code fixes or refactorings. +/// Represents a or . /// -internal interface IFixAllContext +internal interface IRefactorOrFixAllContext { - IFixAllState State { get; } - object Provider { get; } + IRefactorOrFixAllState State { get; } + IRefactorOrFixProvider Provider { get; } CancellationToken CancellationToken { get; } IProgress Progress { get; } - string GetDefaultFixAllTitle(); - IFixAllContext With( + string GetDefaultTitle(); + IRefactorOrFixAllContext With( Optional<(Document? document, Project project)> documentAndProject = default, Optional scope = default, Optional codeActionEquivalenceKey = default, diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllProvider.cs similarity index 55% rename from src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllProvider.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllProvider.cs index 50db400fe36..b540c56f5d9 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllProvider.cs @@ -6,24 +6,16 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Represents a FixAllProvider for code fixes or refactorings. +/// Allows for generic handling of a or . /// -internal interface IFixAllProvider +internal interface IRefactorOrFixAllProvider { - /// - /// Gets the supported scopes for applying multiple occurrences of a code refactoring. - /// By default, it returns the following scopes: - /// (a) - /// (b) and - /// (c) - /// - IEnumerable GetSupportedFixAllScopes(); - - Task GetFixAsync(IFixAllContext fixAllContext); + Task GetCodeActionAsync(IRefactorOrFixAllContext fixAllContext); /// /// The sort of cleanup that should automatically be poerformed for this fix all provider. By default this is diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllState.cs similarity index 71% rename from src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllState.cs index e714ebe17dc..561d10037db 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixAllState.cs @@ -2,17 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Represents internal FixAllState for code fixes or refactorings. +/// Represents internal or . /// -internal interface IFixAllState +internal interface IRefactorOrFixAllState { int CorrelationId { get; } - IFixAllProvider FixAllProvider { get; } + IRefactorOrFixAllProvider FixAllProvider { get; } string? CodeActionEquivalenceKey { get; } FixAllScope Scope { get; } FixAllKind FixAllKind { get; } @@ -23,9 +24,9 @@ internal interface IFixAllState /// /// Underlying code fix provider or code refactoring provider for the fix all occurrences fix. /// - object Provider { get; } + IRefactorOrFixProvider Provider { get; } - IFixAllState With( + IRefactorOrFixAllState With( Optional<(Document? document, Project project)> documentAndProject = default, Optional scope = default, Optional codeActionEquivalenceKey = default); diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs new file mode 100644 index 00000000000..452f5a6da5b --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IRefactorOrFixProvider.cs @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; + +internal interface IRefactorOrFixProvider +{ +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs index 9985293a1b3..d28dd203ede 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringProvider.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; namespace Microsoft.CodeAnalysis.CodeRefactorings; @@ -13,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings; /// Inherit this type to provide source code refactorings. /// Remember to use so the host environment can offer your refactorings in a UI. /// -public abstract class CodeRefactoringProvider +public abstract class CodeRefactoringProvider : IRefactorOrFixProvider { private protected ImmutableArray CustomTags = []; @@ -23,11 +24,11 @@ public abstract class CodeRefactoringProvider public abstract Task ComputeRefactoringsAsync(CodeRefactoringContext context); /// - /// Gets an optional that can apply multiple occurrences of code refactoring(s) - /// registered by this code refactoring provider across the supported s. - /// Return null if the provider doesn't support fix all operation. + /// Gets an optional that can apply multiple occurrences of code refactoring(s) + /// registered by this code refactoring provider across the supported s. + /// Return null if the provider doesn't support the refactor all operation. /// - internal virtual FixAllProvider? GetFixAllProvider() + public virtual RefactorAllProvider? GetRefactorAllProvider() => null; /// diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs deleted file mode 100644 index 1617b9c9453..00000000000 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedFixAllProvider.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; - -namespace Microsoft.CodeAnalysis.CodeRefactorings; - -/// -/// Provides a base class to write a for refactorings that fixes documents independently. -/// This type should be used in the case where the code refactoring(s) only affect individual s. -/// -/// -/// This type provides suitable logic for fixing large solutions in an efficient manner. Projects are serially -/// processed, with all the documents in the project being processed in parallel. -/// is invoked for each document for implementors to process. -/// -/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 -/// -internal abstract class DocumentBasedFixAllProvider(ImmutableArray supportedFixAllScopes) : FixAllProvider -{ - private readonly ImmutableArray _supportedFixAllScopes = supportedFixAllScopes; - - protected DocumentBasedFixAllProvider() - : this(DefaultSupportedFixAllScopes) - { - } - - /// - /// Produce a suitable title for the fix-all this type creates in . Override this if customizing that title is desired. - /// - protected virtual string GetFixAllTitle(FixAllContext fixAllContext) - => fixAllContext.GetDefaultFixAllTitle(); - - /// - /// Apply fix all operation for the code refactoring in the - /// for the given . The document returned will only be examined for its content - /// (e.g. it's or . No other aspects of document (like it's properties), - /// or changes to the or it points at will be considered. - /// - /// The context for the Fix All operation. - /// The document to fix. - /// The spans to fix in the document. If not specified, entire document needs to be fixedd. - /// - /// The new representing the content fixed document. - /// -or- - /// , if no changes were made to the document. - /// - protected abstract Task FixAllAsync(FixAllContext fixAllContext, Document document, Optional> fixAllSpans); - - public sealed override IEnumerable GetSupportedFixAllScopes() - => _supportedFixAllScopes; - - public sealed override Task GetFixAsync(FixAllContext fixAllContext) - => DefaultFixAllProviderHelpers.GetFixAsync( - fixAllContext.GetDefaultFixAllTitle(), fixAllContext, FixAllContextsHelperAsync); - - private Task FixAllContextsHelperAsync(FixAllContext originalFixAllContext, ImmutableArray fixAllContexts) - => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync( - originalFixAllContext, - fixAllContexts, - originalFixAllContext.Progress, - this.GetFixAllTitle(originalFixAllContext), - 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/simplification/etc. Text is returned for documents that don't support syntax. - /// - private async Task GetFixedDocumentsAsync( - FixAllContext fixAllContext, Func onDocumentFixed) - { - Contract.ThrowIfFalse(fixAllContext.Scope is FixAllScope.Document or FixAllScope.Project - or FixAllScope.ContainingMember or FixAllScope.ContainingType); - - var cancellationToken = fixAllContext.CancellationToken; - - // Process all documents in parallel to get the change for each doc. - var documentsAndSpansToFix = await fixAllContext.GetFixAllSpansAsync(cancellationToken).ConfigureAwait(false); - - await Parallel.ForEachAsync( - source: documentsAndSpansToFix, - cancellationToken, - async (tuple, cancellationToken) => - { - var (document, spans) = tuple; - var newDocument = await this.FixAllAsync(fixAllContext, document, spans).ConfigureAwait(false); - await onDocumentFixed(document, newDocument).ConfigureAwait(false); - }).ConfigureAwait(false); - } -} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs new file mode 100644 index 00000000000..44e399d303a --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/DocumentBasedRefactorAllProvider.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings; + +/// +/// Provides a base class to write a for refactorings that refactor documents +/// independently. This type should be used in the case where the code refactoring(s) only affect individual s. +/// +/// +/// This type provides suitable logic for refactorings large solutions in an efficient manner. Projects are serially +/// processed, with all the documents in the project being processed in parallel. is +/// invoked for each document for implementors to process. +/// +/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 +/// +internal abstract class DocumentBasedRefactorAllProvider(ImmutableArray supportedRefactorAllScopes) + : RefactorAllProvider +{ + private readonly ImmutableArray _supportedRefactorAllScopes = supportedRefactorAllScopes; + + protected DocumentBasedRefactorAllProvider() + : this(DefaultSupportedRefactorAllScopes) + { + } + + /// + /// Produce a suitable title for the refactor-all this type creates in . Override this if customizing that title is desired. + /// + protected virtual string GetRefactorAllTitle(RefactorAllContext refactorAllContext) + => refactorAllContext.GetDefaultRefactorAllTitle(); + + /// + /// Apply refactor all operation for the code refactoring in the for the + /// given . The document returned will only be examined for its content (e.g. + /// it's or . No other aspects of document (like it's properties), + /// or changes to the or it points at will be considered. + /// + /// The context for the Refactor All operation. + /// The document to refactor. + /// The spans to refactor in the document. If not specified, entire document needs to be refactored. + /// + /// The new representing the content refactored document. + /// -or- + /// , if no changes were made to the document. + /// + protected abstract Task RefactorAllAsync( + RefactorAllContext refactorAllContext, Document document, Optional> refactorAllSpans); + + public sealed override IEnumerable GetSupportedRefactorAllScopes() + => _supportedRefactorAllScopes; + + public sealed override Task GetRefactoringAsync(RefactorAllContext refactorAllContext) + => DefaultFixAllProviderHelpers.GetFixAsync( + refactorAllContext.GetDefaultRefactorAllTitle(), refactorAllContext, RefactorAllContextsHelperAsync); + + private Task RefactorAllContextsHelperAsync(RefactorAllContext originalRefactorAllContext, ImmutableArray refactorAllContexts) + => DocumentBasedFixAllProviderHelpers.FixAllContextsAsync( + originalRefactorAllContext, + refactorAllContexts, + originalRefactorAllContext.Progress, + this.GetRefactorAllTitle(originalRefactorAllContext), + GetRefactoredDocumentsAsync); + + /// + /// Attempts to apply refactor 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 GetRefactoredDocumentsAsync( + RefactorAllContext refactorAllContext, Func onDocumentRefactored) + { + Contract.ThrowIfFalse(refactorAllContext.Scope is RefactorAllScope.Document or RefactorAllScope.Project + or RefactorAllScope.ContainingMember or RefactorAllScope.ContainingType); + + var cancellationToken = refactorAllContext.CancellationToken; + + // Process all documents in parallel to get the change for each doc. + var documentsAndSpansToRefactor = await refactorAllContext.GetRefactorAllSpansAsync(cancellationToken).ConfigureAwait(false); + + await Parallel.ForEachAsync( + source: documentsAndSpansToRefactor, + cancellationToken, + async (tuple, cancellationToken) => + { + var (document, spans) = tuple; + var newDocument = await this.RefactorAllAsync(refactorAllContext, document, spans).ConfigureAwait(false); + await onDocumentRefactored(document, newDocument).ConfigureAwait(false); + }).ConfigureAwait(false); + } +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs deleted file mode 100644 index 4bb45d22e18..00000000000 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllProvider.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.Text; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; - -namespace Microsoft.CodeAnalysis.CodeRefactorings; - -/// -/// Implement this abstract type to provide fix all occurrences support for code refactorings. -/// -/// -/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 -/// -internal abstract class FixAllProvider : IFixAllProvider -{ - private protected static ImmutableArray DefaultSupportedFixAllScopes - = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution]; - - public virtual IEnumerable GetSupportedFixAllScopes() - => DefaultSupportedFixAllScopes; - - public virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; - - /// - /// Gets fix all occurrences fix for the given fixAllContext. - /// - public abstract Task GetFixAsync(FixAllContext fixAllContext); - - #region IFixAllProvider implementation - Task IFixAllProvider.GetFixAsync(IFixAllContext fixAllContext) - => this.GetFixAsync((FixAllContext)fixAllContext); - #endregion - - /// - /// Create a that fixes documents independently. - /// This can be used in the case where refactoring(s) registered by this provider - /// only affect a single . - /// - /// - /// Callback that will apply the refactorings present in the provided document. The document returned will only be - /// examined for its content (e.g. it's or . No other aspects - /// of it (like attributes), or changes to the or it points at - /// will be considered. - /// - public static FixAllProvider Create(Func>, Task> fixAllAsync) - => Create(fixAllAsync, DefaultSupportedFixAllScopes); - - /// - /// Create a that fixes documents independently. - /// This can be used in the case where refactoring(s) registered by this provider - /// only affect a single . - /// - /// - /// Callback that will apply the refactorings present in the provided document. The document returned will only be - /// examined for its content (e.g. it's or . No other aspects - /// of it (like attributes), or changes to the or it points at - /// will be considered. - /// - /// - /// Supported s for the fix all provider. - /// Note that is not supported by the - /// and should not be part of the supported scopes. - /// - public static FixAllProvider Create( - Func>, Task> fixAllAsync, - ImmutableArray supportedFixAllScopes) - { - return Create(fixAllAsync, supportedFixAllScopes, CodeActionCleanup.Default); - } - - internal static FixAllProvider Create( - Func>, Task> fixAllAsync, - ImmutableArray supportedFixAllScopes, - CodeActionCleanup cleanup) - { - if (fixAllAsync is null) - throw new ArgumentNullException(nameof(fixAllAsync)); - - if (supportedFixAllScopes.IsDefault) - throw new ArgumentNullException(nameof(supportedFixAllScopes)); - - if (supportedFixAllScopes.Contains(FixAllScope.Custom)) - throw new ArgumentException(WorkspacesResources.FixAllScope_Custom_is_not_supported_with_this_API, nameof(supportedFixAllScopes)); - - return new CallbackDocumentBasedFixAllProvider(fixAllAsync, supportedFixAllScopes, cleanup); - } - - private sealed class CallbackDocumentBasedFixAllProvider( - Func>, Task> fixAllAsync, - ImmutableArray supportedFixAllScopes, - CodeActionCleanup cleanup) : DocumentBasedFixAllProvider(supportedFixAllScopes) - { - public override CodeActionCleanup Cleanup { get; } = cleanup; - - protected override Task FixAllAsync(FixAllContext context, Document document, Optional> fixAllSpans) - => fixAllAsync(context, document, fixAllSpans); - } -} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllContext.cs similarity index 50% rename from src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllContext.cs index 7dec1f30e25..dfa4a28d82b 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllContext.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllContext.cs @@ -7,78 +7,83 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Text; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeRefactorings; /// -/// Context for "Fix all occurrences" for code refactorings provided by each . +/// Context for "Refactor all occurrences" for code refactorings provided by each . /// /// /// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 /// -internal sealed class FixAllContext : IFixAllContext +public sealed class RefactorAllContext : IRefactorOrFixAllContext { - internal FixAllState State { get; } + internal RefactorAllState State { get; } - internal FixAllProvider FixAllProvider => State.FixAllProvider; + internal RefactorAllProvider RefactorAllProvider => State.FixAllProvider; /// - /// Document within which fix all occurrences was triggered. + /// Document within which refactor all occurrences was triggered. /// public Document Document => State.Document!; /// - /// Underlying which triggered this fix all. + /// Underlying which triggered this refactor all. /// public CodeRefactoringProvider CodeRefactoringProvider => State.Provider; /// - /// to fix all occurrences. + /// to refactor all occurrences. /// - public FixAllScope Scope => State.Scope; + public RefactorAllScope Scope => (RefactorAllScope)State.Scope; /// - /// The value expected of a participating in this fix all. + /// The value expected of a participating in this + /// refactor all. /// public string? CodeActionEquivalenceKey => State.CodeActionEquivalenceKey; /// - /// CancellationToken for fix all session. + /// CancellationToken for refactor all session. /// public CancellationToken CancellationToken { get; } public IProgress Progress { get; } /// - /// Project to fix all occurrences. - /// Note that this property will always be the containing project of - /// for publicly exposed FixAllContext instance. However, we might create an intermediate FixAllContext - /// with null and non-null Project, so we require this internal property for intermediate computation. + /// Project to refactor all occurrences within. /// public Project Project => State.Project; public Solution Solution => Project.Solution; #region IFixAllContext implementation - IFixAllState IFixAllContext.State => this.State; + IRefactorOrFixAllState IRefactorOrFixAllContext.State => this.State; - object IFixAllContext.Provider => this.CodeRefactoringProvider; + IRefactorOrFixProvider IRefactorOrFixAllContext.Provider => this.CodeRefactoringProvider; - string IFixAllContext.GetDefaultFixAllTitle() => this.GetDefaultFixAllTitle(); + string IRefactorOrFixAllContext.GetDefaultTitle() => this.GetDefaultRefactorAllTitle(); - IFixAllContext IFixAllContext.With( + IRefactorOrFixAllContext IRefactorOrFixAllContext.With( Optional<(Document? document, Project project)> documentAndProject, Optional scope, Optional codeActionEquivalenceKey, Optional cancellationToken) - => this.With(documentAndProject, scope, codeActionEquivalenceKey, cancellationToken); + { + var newState = State.With(documentAndProject, scope, codeActionEquivalenceKey); + var newCancellationToken = cancellationToken.HasValue ? cancellationToken.Value : this.CancellationToken; + + return State == newState && CancellationToken == newCancellationToken + ? this + : new RefactorAllContext(newState, this.Progress, newCancellationToken); + } #endregion - internal FixAllContext( - FixAllState state, + internal RefactorAllContext( + RefactorAllState state, IProgress progressTracker, CancellationToken cancellationToken) { @@ -88,26 +93,12 @@ internal FixAllContext( } /// - /// Gets the spans to fix by document for the for this fix all occurences fix. - /// If no spans are specified, it indicates the entire document needs to be fixed. + /// Gets the spans to refactor by document for the for this refactor all occurrences fix. If no + /// spans are specified, it indicates the entire document needs to be refactored. /// - public Task>>> GetFixAllSpansAsync(CancellationToken cancellationToken) - => State.GetFixAllSpansAsync(cancellationToken); - - internal FixAllContext With( - Optional<(Document? document, Project project)> documentAndProject = default, - Optional scope = default, - Optional codeActionEquivalenceKey = default, - Optional cancellationToken = default) - { - var newState = State.With(documentAndProject, scope, codeActionEquivalenceKey); - var newCancellationToken = cancellationToken.HasValue ? cancellationToken.Value : this.CancellationToken; - - return State == newState && CancellationToken == newCancellationToken - ? this - : new FixAllContext(newState, this.Progress, newCancellationToken); - } + public Task>>> GetRefactorAllSpansAsync(CancellationToken cancellationToken) + => State.GetRefactorAllSpansAsync(cancellationToken); - internal string GetDefaultFixAllTitle() - => FixAllHelper.GetDefaultFixAllTitle(this.Scope, this.State.CodeActionTitle, this.Document, this.Project); + internal string GetDefaultRefactorAllTitle() + => FixAllHelper.GetDefaultFixAllTitle(this.Scope.ToFixAllScope(), this.State.CodeActionTitle, this.Document, this.Project); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.cs new file mode 100644 index 00000000000..1b1d6d29609 --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllProvider.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. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CodeRefactorings; + +/// +/// Implement this abstract type to provide refactor all occurrences support for code refactorings. +/// +/// +/// TODO: Make public, tracked with https://github.com/dotnet/roslyn/issues/60703 +/// +public abstract class RefactorAllProvider : IRefactorOrFixAllProvider +{ + private protected static ImmutableArray DefaultSupportedRefactorAllScopes + = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution]; + + public virtual IEnumerable GetSupportedRefactorAllScopes() + => DefaultSupportedRefactorAllScopes; + + internal virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; + + CodeActionCleanup IRefactorOrFixAllProvider.Cleanup => this.Cleanup; + + /// + /// Gets refactor all occurrences for the given . + /// + public abstract Task GetRefactoringAsync(RefactorAllContext refactorAllContext); + + #region IFixAllProvider implementation + Task IRefactorOrFixAllProvider.GetCodeActionAsync(IRefactorOrFixAllContext fixAllContext) + => this.GetRefactoringAsync((RefactorAllContext)fixAllContext); + #endregion + + /// + /// Create a that refactors documents independently. This can be used in the case + /// where refactoring(s) registered by this provider only affect a single . + /// + /// + /// Callback that will apply the refactorings present in the provided document. The document returned will only be + /// examined for its content (e.g. it's or . No other aspects + /// of it (like attributes), or changes to the or it points at + /// will be considered. + /// + public static RefactorAllProvider Create(Func>, Task> refactorAllAsync) + => Create(refactorAllAsync, DefaultSupportedRefactorAllScopes); + + /// + /// Create a that refactors documents independently. This can be used in the case + /// where refactoring(s) registered by this provider only affect a single . + /// + /// + /// Callback that will apply the refactorings present in the provided document. The document returned will only be + /// examined for its content (e.g. it's or . No other aspects + /// of it (like attributes), or changes to the or it points at + /// will be considered. + /// + /// + /// Supported s for the refactor all provider. + /// Note that is not supported by the + /// and should not be part of the supported scopes. + /// + public static RefactorAllProvider Create( + Func>, Task> refactorAllAsync, + ImmutableArray supportedRefactorAllScopes) + { + return Create(refactorAllAsync, supportedRefactorAllScopes, CodeActionCleanup.Default); + } + + internal static RefactorAllProvider Create( + Func>, Task> refactorAllAsync, + ImmutableArray supportedRefactorAllScopes, + CodeActionCleanup cleanup) + { + if (refactorAllAsync is null) + throw new ArgumentNullException(nameof(refactorAllAsync)); + + if (supportedRefactorAllScopes.IsDefault) + throw new ArgumentNullException(nameof(supportedRefactorAllScopes)); + + if (supportedRefactorAllScopes.Contains(RefactorAllScope.Custom)) + throw new ArgumentException(WorkspacesResources.FixAllScope_Custom_is_not_supported_with_this_API, nameof(supportedRefactorAllScopes)); + + return new CallbackDocumentBasedRefactorAllProvider(refactorAllAsync, supportedRefactorAllScopes, cleanup); + } + + private sealed class CallbackDocumentBasedRefactorAllProvider( + Func>, Task> refactorAllAsync, + ImmutableArray supportedRefactorAllScopes, + CodeActionCleanup cleanup) : DocumentBasedRefactorAllProvider(supportedRefactorAllScopes) + { + internal override CodeActionCleanup Cleanup { get; } = cleanup; + + protected override Task RefactorAllAsync(RefactorAllContext context, Document document, Optional> refactorAllSpans) + => refactorAllAsync(context, document, refactorAllSpans); + } +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs new file mode 100644 index 00000000000..1e9d463cf91 --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllScope.cs @@ -0,0 +1,92 @@ +// 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.Linq; +using Microsoft.CodeAnalysis.CodeFixes; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeRefactorings; + +/// +/// Indicates scope for "Refactor all occurrences" code fixes provided by each . +/// +public enum RefactorAllScope +{ + /// + /// Scope to refactor all occurrences of diagnostic(s) in the entire document. + /// + Document, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the entire project. + /// + Project, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the entire solution. + /// + Solution, + + /// + /// Custom scope to refactor all occurrences of diagnostic(s). This scope can be used by custom s and custom code refactoring engines. + /// + Custom, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the containing member relative to the trigger span for the + /// original code refactoring. + /// + ContainingMember, + + /// + /// Scope to refactor all occurrences of diagnostic(s) in the containing type relative to the trigger span for the + /// original code refactoring. + /// + ContainingType, +} + +internal static class RefactorAllScopeExtensions +{ + static RefactorAllScopeExtensions() + { +#if DEBUG + // Ensures that RefactorAllScope and FixAllScope have the same set of values. + + var refactorFields = typeof(RefactorAllScope) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .Select(f => (f.Name, Value: (int)f.GetValue(null)!)); + + var fixAllFields = typeof(FixAllScope) + .GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static) + .Select(f => (f.Name, Value: (int)f.GetValue(null)!)); + + Contract.ThrowIfFalse(refactorFields.SetEquals(fixAllFields)); +#endif + } + + public static FixAllScope ToFixAllScope(this RefactorAllScope scope) + => scope switch + { + RefactorAllScope.Document => FixAllScope.Document, + RefactorAllScope.Project => FixAllScope.Project, + RefactorAllScope.Solution => FixAllScope.Solution, + RefactorAllScope.Custom => FixAllScope.Custom, + RefactorAllScope.ContainingMember => FixAllScope.ContainingMember, + RefactorAllScope.ContainingType => FixAllScope.ContainingType, + _ => throw ExceptionUtilities.Unreachable(), + }; + + public static RefactorAllScope ToRefactorAllScope(this FixAllScope scope) + => scope switch + { + FixAllScope.Document => RefactorAllScope.Document, + FixAllScope.Project => RefactorAllScope.Project, + FixAllScope.Solution => RefactorAllScope.Solution, + FixAllScope.Custom => RefactorAllScope.Custom, + FixAllScope.ContainingMember => RefactorAllScope.ContainingMember, + FixAllScope.ContainingType => RefactorAllScope.ContainingType, + _ => throw ExceptionUtilities.Unreachable(), + }; +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllState.cs similarity index 51% rename from src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs rename to src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllState.cs index d0e11df8f59..e80efd2df67 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/FixAllState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/FixAllOccurences/RefactorAllState.cs @@ -9,21 +9,20 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeRefactorings; -internal sealed class FixAllState : CommonFixAllState +internal sealed class RefactorAllState : CommonFixAllState { /// - /// Original selection span from which FixAll was invoked. - /// This is used in - /// to compute fix all spans for - /// and scopes. + /// Original selection span from which refactor-all was invoked. This is used in to compute refactor all spans for and scopes. /// private readonly TextSpan _selectionSpan; @@ -31,68 +30,68 @@ internal sealed class FixAllState : CommonFixAllState - /// Gets the spans to fix by document for the for this fix all occurences fix. - /// If no spans are specified, it indicates the entire document needs to be fixed. + /// Gets the spans to refactor by document for the for this refactor all occurrences + /// action. If no spans are specified, it indicates the entire document needs to be refactored. /// - internal async Task>>> GetFixAllSpansAsync(CancellationToken cancellationToken) + internal async Task>>> GetRefactorAllSpansAsync(CancellationToken cancellationToken) { - IEnumerable? documentsToFix = null; - switch (this.Scope) + IEnumerable? documentsToRefactor = null; + switch (this.Scope.ToRefactorAllScope()) { - case FixAllScope.ContainingType or FixAllScope.ContainingMember: + case RefactorAllScope.ContainingType or RefactorAllScope.ContainingMember: Contract.ThrowIfNull(Document); var spanMappingService = Document.GetLanguageService(); if (spanMappingService is null) @@ -103,23 +102,23 @@ internal async Task KeyValuePair.Create(kvp.Key, new Optional>(kvp.Value))) .ToImmutableDictionaryOrEmpty(); - case FixAllScope.Document: + case RefactorAllScope.Document: Contract.ThrowIfNull(Document); - documentsToFix = [Document]; + documentsToRefactor = [Document]; break; - case FixAllScope.Project: - documentsToFix = Project.Documents; + case RefactorAllScope.Project: + documentsToRefactor = Project.Documents; break; - case FixAllScope.Solution: - documentsToFix = Project.Solution.Projects.SelectMany(p => p.Documents); + case RefactorAllScope.Solution: + documentsToRefactor = Project.Solution.Projects.SelectMany(p => p.Documents); break; default: return ImmutableDictionary>>.Empty; } - return documentsToFix.ToImmutableDictionary(d => d, _ => default(Optional>)); + return documentsToRefactor.ToImmutableDictionary(d => d, _ => default(Optional>)); } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs index 0ef5c4888c6..dc567787088 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/CodeRefactorings/SyntaxEditorBasedCodeRefactoringProvider.cs @@ -10,59 +10,58 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeRefactorings; internal abstract partial class SyntaxEditorBasedCodeRefactoringProvider : CodeRefactoringProvider { - protected static readonly ImmutableArray DefaultFixAllScopes = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution]; - protected static readonly ImmutableArray AllFixAllScopes = [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution, FixAllScope.ContainingType, FixAllScope.ContainingMember]; + protected static readonly ImmutableArray DefaultRefactorAllScopes = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution]; + protected static readonly ImmutableArray AllRefactorAllScopes = [RefactorAllScope.Document, RefactorAllScope.Project, RefactorAllScope.Solution, RefactorAllScope.ContainingType, RefactorAllScope.ContainingMember]; - protected abstract ImmutableArray SupportedFixAllScopes { get; } + protected abstract ImmutableArray SupportedRefactorAllScopes { get; } protected virtual CodeActionCleanup Cleanup => CodeActionCleanup.Default; - internal sealed override FixAllProvider? GetFixAllProvider() + public sealed override RefactorAllProvider? GetRefactorAllProvider() { - if (SupportedFixAllScopes.IsEmpty) + if (SupportedRefactorAllScopes.IsEmpty) return null; - return FixAllProvider.Create( - async (fixAllContext, document, fixAllSpans) => - await this.FixAllAsync(document, fixAllSpans, fixAllContext.CodeActionEquivalenceKey, fixAllContext.CancellationToken).ConfigureAwait(false), - SupportedFixAllScopes, + return RefactorAllProvider.Create( + async (refactorAllContext, document, refactorAllSpans) => + await this.RefactorAllAsync(document, refactorAllSpans, refactorAllContext.CodeActionEquivalenceKey, refactorAllContext.CancellationToken).ConfigureAwait(false), + SupportedRefactorAllScopes, this.Cleanup); } - protected Task FixAsync( + protected Task RefactorAsync( Document document, - TextSpan fixAllSpan, + TextSpan refactorAllSpan, string? equivalenceKey, CancellationToken cancellationToken) { - return FixAllWithEditorAsync(document, - editor => FixAllAsync(document, [fixAllSpan], editor, equivalenceKey, cancellationToken), + return RefactorAllWithEditorAsync(document, + editor => RefactorAllAsync(document, [refactorAllSpan], editor, equivalenceKey, cancellationToken), cancellationToken); } - protected Task FixAllAsync( + protected Task RefactorAllAsync( Document document, - Optional> fixAllSpans, + Optional> refactorAllSpans, string? equivalenceKey, CancellationToken cancellationToken) { - return FixAllWithEditorAsync(document, FixAllAsync, cancellationToken); + return RefactorAllWithEditorAsync(document, RefactorAllAsync, cancellationToken); // Local functions - Task FixAllAsync(SyntaxEditor editor) + Task RefactorAllAsync(SyntaxEditor editor) { - // Fix the entire document if there are no sub-spans to fix. - var spans = fixAllSpans.HasValue ? fixAllSpans.Value : [editor.OriginalRoot.FullSpan]; - return this.FixAllAsync(document, spans, editor, equivalenceKey, cancellationToken); + // Refactor the entire document if there are no sub-spans to refactor. + var spans = refactorAllSpans.HasValue ? refactorAllSpans.Value : [editor.OriginalRoot.FullSpan]; + return this.RefactorAllAsync(document, spans, editor, equivalenceKey, cancellationToken); } } - internal static async Task FixAllWithEditorAsync( + internal static async Task RefactorAllWithEditorAsync( Document document, Func editAsync, CancellationToken cancellationToken) @@ -76,9 +75,9 @@ internal static async Task FixAllWithEditorAsync( return document.WithSyntaxRoot(newRoot); } - protected abstract Task FixAllAsync( + protected abstract Task RefactorAllAsync( Document document, - ImmutableArray fixAllSpans, + ImmutableArray refactorAllSpans, SyntaxEditor editor, string? equivalenceKey, CancellationToken cancellationToken); diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalysisResultPair.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalysisResultPair.cs deleted file mode 100644 index c7635ccb521..00000000000 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalysisResultPair.cs +++ /dev/null @@ -1,226 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Collections.Generic; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal sealed class AnalysisResultPair -{ - public AnalysisResultPair(AnalysisResult? projectAnalysisResult, AnalysisResult? hostAnalysisResult) - { - if (projectAnalysisResult is not null && hostAnalysisResult is not null) - { - } - else - { - Contract.ThrowIfTrue(projectAnalysisResult is null && hostAnalysisResult is null); - } - - ProjectAnalysisResult = projectAnalysisResult; - HostAnalysisResult = hostAnalysisResult; - } - - public AnalysisResult? ProjectAnalysisResult { get; } - - public AnalysisResult? HostAnalysisResult { get; } - - public ImmutableDictionary>> MergedSyntaxDiagnostics - { - get - { - return InterlockedOperations.Initialize( - ref field, - static arg => - { - if (arg.projectDiagnostics is null) - { - // project and host diagnostics cannot both be null - Contract.ThrowIfNull(arg.hostDiagnostics); - return arg.hostDiagnostics; - } - else if (arg.hostDiagnostics is null) - { - return arg.projectDiagnostics; - } - - return MergeDiagnostics(arg.projectDiagnostics, arg.hostDiagnostics); - }, - (projectDiagnostics: ProjectAnalysisResult?.SyntaxDiagnostics, hostDiagnostics: HostAnalysisResult?.SyntaxDiagnostics)); - } - } - - public ImmutableDictionary>> MergedSemanticDiagnostics - { - get - { - return InterlockedOperations.Initialize( - ref field, - static arg => - { - if (arg.projectDiagnostics is null) - { - // project and host diagnostics cannot both be null - Contract.ThrowIfNull(arg.hostDiagnostics); - return arg.hostDiagnostics; - } - else if (arg.hostDiagnostics is null) - { - return arg.projectDiagnostics; - } - - return MergeDiagnostics(arg.projectDiagnostics, arg.hostDiagnostics); - }, - (projectDiagnostics: ProjectAnalysisResult?.SemanticDiagnostics, hostDiagnostics: HostAnalysisResult?.SemanticDiagnostics)); - } - } - - public ImmutableDictionary>> MergedAdditionalFileDiagnostics - { - get - { - return InterlockedOperations.Initialize( - ref field, - static arg => - { - if (arg.projectDiagnostics is null) - { - // project and host diagnostics cannot both be null - Contract.ThrowIfNull(arg.hostDiagnostics); - return arg.hostDiagnostics; - } - else if (arg.hostDiagnostics is null) - { - return arg.projectDiagnostics; - } - - return MergeDiagnostics(arg.projectDiagnostics, arg.hostDiagnostics); - }, - (projectDiagnostics: ProjectAnalysisResult?.AdditionalFileDiagnostics, hostDiagnostics: HostAnalysisResult?.AdditionalFileDiagnostics)); - } - } - - public ImmutableDictionary> MergedCompilationDiagnostics - { - get - { - return InterlockedOperations.Initialize( - ref field, - static arg => - { - if (arg.projectDiagnostics is null) - { - // project and host diagnostics cannot both be null - Contract.ThrowIfNull(arg.hostDiagnostics); - return arg.hostDiagnostics; - } - else if (arg.hostDiagnostics is null) - { - return arg.projectDiagnostics; - } - - return MergeDiagnostics(arg.projectDiagnostics, arg.hostDiagnostics); - }, - (projectDiagnostics: ProjectAnalysisResult?.CompilationDiagnostics, hostDiagnostics: HostAnalysisResult?.CompilationDiagnostics)); - } - } - - public ImmutableDictionary MergedAnalyzerTelemetryInfo - { - get - { - return InterlockedOperations.Initialize( - ref field, - static arg => - { - if (arg.projectTelemetryInfo is null) - { - // project and host telemetry cannot both be null - Contract.ThrowIfNull(arg.hostTelemetryInfo); - return arg.hostTelemetryInfo; - } - else if (arg.hostTelemetryInfo is null) - { - return arg.projectTelemetryInfo; - } - - return MergeTelemetry(arg.projectTelemetryInfo, arg.hostTelemetryInfo); - }, - (projectTelemetryInfo: ProjectAnalysisResult?.AnalyzerTelemetryInfo, hostTelemetryInfo: HostAnalysisResult?.AnalyzerTelemetryInfo)); - } - - private set; - } - - public static AnalysisResultPair? FromResult(AnalysisResult? projectAnalysisResult, AnalysisResult? hostAnalysisResult) - { - if (projectAnalysisResult is null && hostAnalysisResult is null) - return null; - - return new AnalysisResultPair(projectAnalysisResult, hostAnalysisResult); - } - - private static ImmutableDictionary>> MergeDiagnostics( - ImmutableDictionary>> first, - ImmutableDictionary>> second) - where TKey : class - { - var localSyntaxDiagnostics = first.ToBuilder(); - foreach (var (tree, treeDiagnostics) in second) - { - if (!localSyntaxDiagnostics.TryGetValue(tree, out var projectSyntaxDiagnostics)) - { - localSyntaxDiagnostics.Add(tree, treeDiagnostics); - continue; - } - - localSyntaxDiagnostics[tree] = MergeDiagnostics(projectSyntaxDiagnostics, treeDiagnostics); - } - - return localSyntaxDiagnostics.ToImmutable(); - } - - private static ImmutableDictionary> MergeDiagnostics( - ImmutableDictionary> first, - ImmutableDictionary> second) - { - var analyzerToDiagnostics = first.ToBuilder(); - foreach (var (analyzer, diagnostics) in second) - { - if (!analyzerToDiagnostics.TryGetValue(analyzer, out var firstDiagnostics)) - { - analyzerToDiagnostics.Add(analyzer, diagnostics); - continue; - } - - analyzerToDiagnostics[analyzer] = firstDiagnostics.AddRange(diagnostics); - } - - return analyzerToDiagnostics.ToImmutable(); - } - - private static ImmutableDictionary MergeTelemetry( - ImmutableDictionary first, - ImmutableDictionary second) - { - var analyzerToDiagnostics = first.ToBuilder(); - foreach (var (analyzer, telemetry) in second) - { - if (!analyzerToDiagnostics.TryGetValue(analyzer, out var firstTelemetry)) - { - analyzerToDiagnostics.Add(analyzer, telemetry); - continue; - } - - // For telemetry info, keep whichever instance had the longest time - if (telemetry.ExecutionTime > firstTelemetry.ExecutionTime) - analyzerToDiagnostics[analyzer] = telemetry; - } - - return analyzerToDiagnostics.ToImmutable(); - } -} diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs new file mode 100644 index 00000000000..6fd1ebb342d --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/AnalyzerFilter.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +[Flags] +internal enum AnalyzerFilter +{ + /// + /// The default 'compiler analyzer' which reports the standard set of compiler diagnostics. + /// + CompilerAnalyzer = 1, + + /// + /// Any other analyzer that is not the default 'compiler analyzer'. + /// + NonCompilerAnalyzer = 2, + + /// + /// Include both compiler and non-compiler analyzers. + /// + All = CompilerAnalyzer | NonCompilerAnalyzer, +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs index bf31bc4a0b0..95ae3c00eea 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs @@ -9,5 +9,5 @@ namespace Microsoft.CodeAnalysis; [EventSource(Name = "Microsoft-CodeAnalysis-Workspaces")] internal sealed partial class CodeAnalysisEventSource { - public static readonly CodeAnalysisEventSource Log = new CodeAnalysisEventSource(); + public static readonly CodeAnalysisEventSource Log = new(); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs deleted file mode 100644 index a532074b3fa..00000000000 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/CompilationWithAnalyzersPair.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal sealed class CompilationWithAnalyzersPair -{ - public CompilationWithAnalyzersPair(CompilationWithAnalyzers? projectCompilationWithAnalyzers, CompilationWithAnalyzers? hostCompilationWithAnalyzers) - { - if (projectCompilationWithAnalyzers is not null && hostCompilationWithAnalyzers is not null) - { - Contract.ThrowIfFalse(projectCompilationWithAnalyzers.AnalysisOptions.ReportSuppressedDiagnostics == hostCompilationWithAnalyzers.AnalysisOptions.ReportSuppressedDiagnostics); - Contract.ThrowIfFalse(projectCompilationWithAnalyzers.AnalysisOptions.ConcurrentAnalysis == hostCompilationWithAnalyzers.AnalysisOptions.ConcurrentAnalysis); - } - else - { - Contract.ThrowIfTrue(projectCompilationWithAnalyzers is null && hostCompilationWithAnalyzers is null); - } - - ProjectCompilationWithAnalyzers = projectCompilationWithAnalyzers; - HostCompilationWithAnalyzers = hostCompilationWithAnalyzers; - } - - public Compilation? ProjectCompilation => ProjectCompilationWithAnalyzers?.Compilation; - - public Compilation? HostCompilation => HostCompilationWithAnalyzers?.Compilation; - - public CompilationWithAnalyzers? ProjectCompilationWithAnalyzers { get; } - - public CompilationWithAnalyzers? HostCompilationWithAnalyzers { get; } - - public bool ConcurrentAnalysis => ProjectCompilationWithAnalyzers?.AnalysisOptions.ConcurrentAnalysis ?? HostCompilationWithAnalyzers!.AnalysisOptions.ConcurrentAnalysis; - - public bool HasAnalyzers => ProjectAnalyzers.Any() || HostAnalyzers.Any(); - - public ImmutableArray ProjectAnalyzers => ProjectCompilationWithAnalyzers?.Analyzers ?? []; - - public ImmutableArray HostAnalyzers => HostCompilationWithAnalyzers?.Analyzers ?? []; - - public Task GetAnalyzerTelemetryInfoAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken) - { - if (ProjectAnalyzers.Contains(analyzer)) - { - return ProjectCompilationWithAnalyzers!.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken); - } - else - { - Debug.Assert(HostAnalyzers.Contains(analyzer)); - return HostCompilationWithAnalyzers!.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken); - } - } - - public async Task GetAnalysisResultAsync(CancellationToken cancellationToken) - { - var projectAnalysisResult = ProjectCompilationWithAnalyzers is not null - ? await ProjectCompilationWithAnalyzers.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false) - : null; - var hostAnalysisResult = HostCompilationWithAnalyzers is not null - ? await HostCompilationWithAnalyzers.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false) - : null; - - return AnalysisResultPair.FromResult(projectAnalysisResult, hostAnalysisResult); - } - - public async Task GetAnalysisResultAsync(SyntaxTree tree, TextSpan? filterSpan, ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers, CancellationToken cancellationToken) - { - var projectAnalysisResult = projectAnalyzers.Any() - ? await ProjectCompilationWithAnalyzers!.GetAnalysisResultAsync(tree, filterSpan, projectAnalyzers, cancellationToken).ConfigureAwait(false) - : null; - var hostAnalysisResult = hostAnalyzers.Any() - ? await HostCompilationWithAnalyzers!.GetAnalysisResultAsync(tree, filterSpan, hostAnalyzers, cancellationToken).ConfigureAwait(false) - : null; - - return AnalysisResultPair.FromResult(projectAnalysisResult, hostAnalysisResult); - } - - public async Task GetAnalysisResultAsync(AdditionalText file, TextSpan? filterSpan, ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers, CancellationToken cancellationToken) - { - var projectAnalysisResult = projectAnalyzers.Any() - ? await ProjectCompilationWithAnalyzers!.GetAnalysisResultAsync(file, filterSpan, projectAnalyzers, cancellationToken).ConfigureAwait(false) - : null; - var hostAnalysisResult = hostAnalyzers.Any() - ? await HostCompilationWithAnalyzers!.GetAnalysisResultAsync(file, filterSpan, hostAnalyzers, cancellationToken).ConfigureAwait(false) - : null; - - return AnalysisResultPair.FromResult(projectAnalysisResult, hostAnalysisResult); - } - - public async Task GetAnalysisResultAsync(SemanticModel model, TextSpan? filterSpan, ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers, CancellationToken cancellationToken) - { - var projectAnalysisResult = projectAnalyzers.Any() - ? await ProjectCompilationWithAnalyzers!.GetAnalysisResultAsync(model, filterSpan, projectAnalyzers, cancellationToken).ConfigureAwait(false) - : null; - var hostAnalysisResult = hostAnalyzers.Any() - ? await HostCompilationWithAnalyzers!.GetAnalysisResultAsync(model, filterSpan, hostAnalyzers, cancellationToken).ConfigureAwait(false) - : null; - - return AnalysisResultPair.FromResult(projectAnalysisResult, hostAnalysisResult); - } -} diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs index 99586494d07..4397f6fa10f 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerInfoCache.cs @@ -32,7 +32,7 @@ internal sealed partial class DiagnosticAnalyzerInfoCache /// The purpose of this map is to avoid multiple calls to that might return different values /// (they should not but we need a guarantee to function correctly). /// - private readonly ConditionalWeakTable _descriptorsInfo; + private readonly ConditionalWeakTable _descriptorsInfo = new(); /// /// Supported suppressions of each . @@ -44,13 +44,13 @@ internal sealed partial class DiagnosticAnalyzerInfoCache /// The purpose of this map is to avoid multiple calls to that might return different values /// (they should not but we need a guarantee to function correctly). /// - private readonly ConditionalWeakTable _suppressionsInfo; + private readonly ConditionalWeakTable _suppressionsInfo = new(); /// /// Lazily populated map from diagnostic IDs to diagnostic descriptor. /// If same diagnostic ID is reported by multiple descriptors, a null value is stored in the map for that ID. /// - private readonly ConcurrentDictionary _idToDescriptorsMap; + private readonly ConcurrentDictionary _idToDescriptorsMap = []; private sealed class DiagnosticDescriptorsInfo(ImmutableArray supportedDescriptors, bool telemetryAllowed) { @@ -64,25 +64,6 @@ private sealed class SuppressionDescriptorsInfo(ImmutableArray SupportedSuppressions = supportedSuppressions; } - [Export, Shared] - internal sealed class SharedGlobalCache - { - public readonly DiagnosticAnalyzerInfoCache AnalyzerInfoCache = new(); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SharedGlobalCache() - { - } - } - - internal DiagnosticAnalyzerInfoCache() - { - _descriptorsInfo = new(); - _suppressionsInfo = new(); - _idToDescriptorsMap = []; - } - /// /// Returns of given . /// diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index 00233f4dade..2a9a99a59d3 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; @@ -202,10 +203,27 @@ void GetLocationInfo(out FileLinePositionSpan originalLineInfo, out FileLinePosi } } - public static DiagnosticData Create(Diagnostic diagnostic, Project project) - => Create(diagnostic, project.Id, project.Language, + [return: NotNullIfNotNull(nameof(diagnostic))] + public static DiagnosticData? Create(Diagnostic? diagnostic, Project project) + { + if (diagnostic is null) + return null; + + var document = project.GetDocument(diagnostic.Location.SourceTree); + if (document != null) + return Create(diagnostic, document); + + if (diagnostic.Location.Kind == LocationKind.ExternalFile) + { + document = project.Documents.FirstOrDefault(d => d.FilePath == diagnostic.Location.GetLineSpan().Path); + if (document != null) + return Create(diagnostic, document); + } + + return Create(diagnostic, project.Id, project.Language, location: new DiagnosticDataLocation(new FileLinePositionSpan(project.FilePath ?? project.Solution.FilePath ?? "", span: default)), additionalLocations: default, additionalProperties: null); + } public static DiagnosticData Create(Diagnostic diagnostic, TextDocument document) { diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs new file mode 100644 index 00000000000..ddc7b0c5d9c --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticIdFilter.cs @@ -0,0 +1,65 @@ +// 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.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.Diagnostics; + +/// +/// If present, if an analyzer has at least one descriptor in this set, it will be included. +/// Note: this set can include diagnostic IDs from multiple analyzers in it. +/// +/// +/// If present, if all of the descriptors an analyzer has is in this set, it will be excluded. +/// Note: this set can include diagnostic IDs from multiple analyzers in it. +/// +[DataContract] +internal readonly record struct DiagnosticIdFilter( + [property: DataMember(Order = 0)] ImmutableHashSet? IncludedDiagnosticIds, + [property: DataMember(Order = 1)] ImmutableHashSet? ExcludedDiagnosticIds) +{ + public static readonly DiagnosticIdFilter All = default; + + public static DiagnosticIdFilter Include(ImmutableHashSet? includedDiagnosticIds) + => new(includedDiagnosticIds, ExcludedDiagnosticIds: null); + + public static DiagnosticIdFilter Exclude(ImmutableHashSet excludedDiagnosticIds) + => new(IncludedDiagnosticIds: null, excludedDiagnosticIds); + + /// + /// Checks the IDs from a single analyzer's to see if it is + /// allowed by this filter. If this is , this will always return true. If there are any + /// values in , at least one of the IDs must be in that set. If there are any + /// values in , not all of the IDs can be in that set. + /// + public bool Allow(params IEnumerable ids) + { + if (this == All) + return true; + + foreach (var id in ids) + { + // If the ID is in the included set, then that's good enough as the semantics for this type are that as long + // as we see one allowed id, we allow the analyzer. + if (IncludedDiagnosticIds != null && + IncludedDiagnosticIds.Contains(id)) + { + return true; + } + + // If the ID is *not* in the excluded set, then that's good enough as the semantics for this type are that we + // only filter out the analyzers that have *all* their IDs in the excluded set. + if (ExcludedDiagnosticIds != null && + !ExcludedDiagnosticIds.Contains(id)) + { + return true; + } + } + + return false; + } +} diff --git a/src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticKind.cs similarity index 100% rename from src/roslyn/src/Features/Core/Portable/Diagnostics/DiagnosticKind.cs rename to src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DiagnosticKind.cs diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DocumentAnalysisScope.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DocumentAnalysisScope.cs index a0a4bfae6cf..59832a12f82 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DocumentAnalysisScope.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/DocumentAnalysisScope.cs @@ -21,19 +21,15 @@ internal sealed class DocumentAnalysisScope public DocumentAnalysisScope( TextDocument document, TextSpan? span, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, + ImmutableArray analyzers, AnalysisKind kind) { Debug.Assert(kind is AnalysisKind.Syntax or AnalysisKind.Semantic); - Debug.Assert(!projectAnalyzers.IsDefault); - Debug.Assert(!hostAnalyzers.IsDefault); - Debug.Assert(!projectAnalyzers.IsEmpty || !hostAnalyzers.IsEmpty); + Debug.Assert(!analyzers.IsDefaultOrEmpty); TextDocument = document; Span = span; - ProjectAnalyzers = projectAnalyzers; - HostAnalyzers = hostAnalyzers; + Analyzers = analyzers; Kind = kind; _lazyAdditionalFile = new Lazy(ComputeAdditionalFile); @@ -41,8 +37,7 @@ public DocumentAnalysisScope( public TextDocument TextDocument { get; } public TextSpan? Span { get; } - public ImmutableArray ProjectAnalyzers { get; } - public ImmutableArray HostAnalyzers { get; } + public ImmutableArray Analyzers { get; } public AnalysisKind Kind { get; } /// @@ -60,8 +55,8 @@ private AdditionalText ComputeAdditionalFile() } public DocumentAnalysisScope WithSpan(TextSpan? span) - => new(TextDocument, span, ProjectAnalyzers, HostAnalyzers, Kind); + => new(TextDocument, span, Analyzers, Kind); - public DocumentAnalysisScope WithAnalyzers(ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers) - => new(TextDocument, Span, projectAnalyzers, hostAnalyzers, Kind); + public DocumentAnalysisScope WithAnalyzers(ImmutableArray analyzers) + => new(TextDocument, Span, analyzers, Kind); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs index 60d0f3dc455..cbd469cb790 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; @@ -104,12 +103,11 @@ public static bool IsFeaturesAnalyzer(this AnalyzerReference reference) } public static async Task> ToResultBuilderMapAsync( - this AnalysisResultPair analysisResult, + this AnalysisResult analysisResult, ImmutableArray additionalPragmaSuppressionDiagnostics, DocumentAnalysisScope? documentAnalysisScope, Project project, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, + ImmutableArray analyzers, SkippedHostAnalyzersInfo skippedAnalyzersInfo, CancellationToken cancellationToken) { @@ -128,7 +126,7 @@ public static async Task(); - foreach (var analyzer in projectAnalyzers.ConcatFast(hostAnalyzers)) + foreach (var analyzer in analyzers) { if (builder.ContainsKey(analyzer)) { @@ -161,13 +159,13 @@ public static async Task Filter( filterSpan.HasValue && !filterSpan.Value.IntersectsWith(diagnostic.Location.SourceSpan)); } - public static async Task<(AnalysisResultPair? analysisResult, ImmutableArray additionalDiagnostics)> GetAnalysisResultAsync( - this CompilationWithAnalyzersPair compilationWithAnalyzers, - DocumentAnalysisScope? documentAnalysisScope, - Project project, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - CancellationToken cancellationToken) - { - var result = await GetAnalysisResultAsync(compilationWithAnalyzers, documentAnalysisScope, cancellationToken).ConfigureAwait(false); - var additionalDiagnostics = await compilationWithAnalyzers.GetPragmaSuppressionAnalyzerDiagnosticsAsync( - documentAnalysisScope, project, analyzerInfoCache, cancellationToken).ConfigureAwait(false); - return (result, additionalDiagnostics); - } - - private static async Task GetAnalysisResultAsync( - CompilationWithAnalyzersPair compilationWithAnalyzers, - DocumentAnalysisScope? documentAnalysisScope, - CancellationToken cancellationToken) - { - if (documentAnalysisScope == null) - { - return await compilationWithAnalyzers.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); - } - - Debug.Assert(documentAnalysisScope.ProjectAnalyzers.ToSet().IsSubsetOf(compilationWithAnalyzers.ProjectAnalyzers)); - Debug.Assert(documentAnalysisScope.HostAnalyzers.ToSet().IsSubsetOf(compilationWithAnalyzers.HostAnalyzers)); - - switch (documentAnalysisScope.Kind) - { - case AnalysisKind.Syntax: - if (documentAnalysisScope.TextDocument is Document document) - { - var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - return await compilationWithAnalyzers.GetAnalysisResultAsync(tree, documentAnalysisScope.Span, documentAnalysisScope.ProjectAnalyzers, documentAnalysisScope.HostAnalyzers, cancellationToken).ConfigureAwait(false); - } - else - { - return await compilationWithAnalyzers.GetAnalysisResultAsync(documentAnalysisScope.AdditionalFile, documentAnalysisScope.Span, documentAnalysisScope.ProjectAnalyzers, documentAnalysisScope.HostAnalyzers, cancellationToken).ConfigureAwait(false); - } - - case AnalysisKind.Semantic: - var model = await ((Document)documentAnalysisScope.TextDocument).GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - return await compilationWithAnalyzers.GetAnalysisResultAsync(model, documentAnalysisScope.Span, documentAnalysisScope.ProjectAnalyzers, documentAnalysisScope.HostAnalyzers, cancellationToken).ConfigureAwait(false); - - default: - throw ExceptionUtilities.UnexpectedValue(documentAnalysisScope.Kind); - } - } - - private static async Task> GetPragmaSuppressionAnalyzerDiagnosticsAsync( - this CompilationWithAnalyzersPair compilationWithAnalyzers, - DocumentAnalysisScope? documentAnalysisScope, - Project project, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - CancellationToken cancellationToken) - { - var hostAnalyzers = documentAnalysisScope?.HostAnalyzers ?? compilationWithAnalyzers.HostAnalyzers; - var suppressionAnalyzer = hostAnalyzers.OfType().FirstOrDefault(); - if (suppressionAnalyzer == null) - return []; - - RoslynDebug.AssertNotNull(compilationWithAnalyzers.HostCompilationWithAnalyzers); - - if (documentAnalysisScope != null) - { - if (documentAnalysisScope.TextDocument is not Document document) - return []; - - using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); - await AnalyzeDocumentAsync( - compilationWithAnalyzers.HostCompilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer, - document, documentAnalysisScope.Span, diagnosticsBuilder.Add, cancellationToken).ConfigureAwait(false); - return diagnosticsBuilder.ToImmutableAndClear(); - } - else - { - if (compilationWithAnalyzers.ConcurrentAnalysis) - { - return await ProducerConsumer.RunParallelAsync( - source: project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken), - produceItems: static async (document, callback, args, cancellationToken) => - { - var (hostCompilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer) = args; - await AnalyzeDocumentAsync( - hostCompilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer, - document, span: null, callback, cancellationToken).ConfigureAwait(false); - }, - args: (compilationWithAnalyzers.HostCompilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer), - cancellationToken).ConfigureAwait(false); - } - else - { - using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); - await foreach (var document in project.GetAllRegularAndSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) - { - await AnalyzeDocumentAsync( - compilationWithAnalyzers.HostCompilationWithAnalyzers, analyzerInfoCache, suppressionAnalyzer, - document, span: null, diagnosticsBuilder.Add, cancellationToken).ConfigureAwait(false); - } - - return diagnosticsBuilder.ToImmutableAndClear(); - } - } - - static async Task AnalyzeDocumentAsync( - CompilationWithAnalyzers hostCompilationWithAnalyzers, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - IPragmaSuppressionsAnalyzer suppressionAnalyzer, - Document document, - TextSpan? span, - Action reportDiagnostic, - CancellationToken cancellationToken) - { - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - await suppressionAnalyzer.AnalyzeAsync( - semanticModel, span, hostCompilationWithAnalyzers, analyzerInfoCache.GetDiagnosticDescriptors, reportDiagnostic, cancellationToken).ConfigureAwait(false); - } - } - /// /// Calculates a checksum that contains a project's checksum along with a checksum for each of the project's /// transitive dependencies. @@ -567,24 +447,4 @@ public static bool IsReportedInDocument(Diagnostic diagnostic, TextDocument targ return false; } - - public static ImmutableArray FilterAnalyzers( - this ImmutableArray analyzers, - ImmutableHashSet analyzerIds) - { - using var _ = PooledDictionary.GetInstance(out var analyzerMap); - foreach (var analyzer in analyzers) - { - // In the case of multiple analyzers with the same ID, we keep the last one. - var analyzerId = analyzer.GetAnalyzerId(); - if (analyzerIds.Contains(analyzerId)) - analyzerMap[analyzerId] = analyzer; - } - - var result = new FixedSizeArrayBuilder(analyzerMap.Count); - foreach (var (_, analyzer) in analyzerMap) - result.Add(analyzer); - - return result.MoveToImmutable(); - } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs index fb72eccc2dc..f72d4e1095e 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/HostDiagnosticAnalyzers.cs @@ -75,18 +75,19 @@ internal HostDiagnosticAnalyzers(IReadOnlyList hostAnalyzerRe public ImmutableDictionary> GetOrCreateHostDiagnosticAnalyzersPerReference(string language) => _hostDiagnosticAnalyzersPerLanguageMap.GetOrAdd(language, CreateHostDiagnosticAnalyzersAndBuildMap); - public ImmutableDictionary> GetDiagnosticDescriptorsPerReference(DiagnosticAnalyzerInfoCache infoCache) + public ImmutableDictionary> GetDiagnosticDescriptorsPerReference( + DiagnosticAnalyzerInfoCache infoCache, + Project? project) { - return ConvertReferenceIdentityToName( - CreateDiagnosticDescriptorsPerReference(infoCache, _lazyHostDiagnosticAnalyzersPerReferenceMap.Value), - _hostAnalyzerReferencesMap); - } + var descriptorsPerReference = project is null + ? CreateDiagnosticDescriptorsPerReference(infoCache, _lazyHostDiagnosticAnalyzersPerReferenceMap.Value) + : CreateDiagnosticDescriptorsPerReference(infoCache, CreateDiagnosticAnalyzersPerReference(project)); - public ImmutableDictionary> GetDiagnosticDescriptorsPerReference(DiagnosticAnalyzerInfoCache infoCache, Project project) - { - var descriptorPerReference = CreateDiagnosticDescriptorsPerReference(infoCache, CreateDiagnosticAnalyzersPerReference(project)); - var map = _hostAnalyzerReferencesMap.AddRange(CreateProjectAnalyzerReferencesMap(project.AnalyzerReferences)); - return ConvertReferenceIdentityToName(descriptorPerReference, map); + var map = project is null + ? _hostAnalyzerReferencesMap + : _hostAnalyzerReferencesMap.AddRange(CreateProjectAnalyzerReferencesMap(project.AnalyzerReferences)); + + return ConvertReferenceIdentityToName(descriptorsPerReference, map); } private static ImmutableDictionary> ConvertReferenceIdentityToName( diff --git a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs index c4a322cdcea..2675e961f7b 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs @@ -14,35 +14,33 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal interface IRemoteDiagnosticAnalyzerService { - /// - /// Returns the analyzers that are candidates to be de-prioritized to - /// priority for improvement in analyzer - /// execution performance for priority buckets above 'Low' priority. - /// Based on performance measurements, currently only analyzers which register SymbolStart/End actions - /// or SemanticModel actions are considered candidates to be de-prioritized. However, these semantics - /// could be changed in future based on performance measurements. - /// - ValueTask> GetDeprioritizationCandidatesAsync( - Checksum solutionChecksum, ProjectId projectId, ImmutableHashSet analyzerIds, CancellationToken cancellationToken); + ValueTask> ForceRunCodeAnalysisDiagnosticsAsync( + Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); + + ValueTask IsAnyDiagnosticIdDeprioritizedAsync( + Checksum solutionChecksum, ProjectId projectId, ImmutableArray diagnosticIds, CancellationToken cancellationToken); - ValueTask> ComputeDiagnosticsAsync( - Checksum solutionChecksum, DocumentId documentId, TextSpan? range, - ImmutableHashSet allAnalyzerIds, - ImmutableHashSet syntaxAnalyzersIds, - ImmutableHashSet semanticSpanAnalyzersIds, - ImmutableHashSet semanticDocumentAnalyzersIds, - bool incrementalAnalysis, - bool logPerformanceInfo, + ValueTask> GetDiagnosticsForSpanAsync( + Checksum solutionChecksum, + DocumentId documentId, + TextSpan? range, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, + DiagnosticKind diagnosticKind, CancellationToken cancellationToken); - ValueTask> ProduceProjectDiagnosticsAsync( + ValueTask> GetDiagnosticsForIdsAsync( Checksum solutionChecksum, ProjectId projectId, - ImmutableHashSet analyzerIds, - ImmutableHashSet? diagnosticIds, ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics, - bool includeProjectNonLocalResult, + CancellationToken cancellationToken); + + ValueTask> GetProjectDiagnosticsForIdsAsync( + Checksum solutionChecksum, ProjectId projectId, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, CancellationToken cancellationToken); ValueTask> GetSourceGeneratorDiagnosticsAsync(Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); @@ -50,11 +48,11 @@ ValueTask> ProduceProjectDiagnosticsAsync( ValueTask> GetDiagnosticDescriptorsAsync( Checksum solutionChecksum, ProjectId projectId, string analyzerReferenceFullPath, string language, CancellationToken cancellationToken); - ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( + ValueTask> GetCompilationEndDiagnosticDescriptorIdsAsync( Checksum solutionChecksum, CancellationToken cancellationToken); ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( - Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); + Checksum solutionChecksum, ProjectId? projectId, CancellationToken cancellationToken); } [DataContract] diff --git a/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 6ed520b9011..2841cd9f062 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; @@ -683,6 +684,14 @@ internal abstract SyntaxNode EnumDeclaration( DeclarationModifiers modifiers = default, IEnumerable? members = null); + /// + /// Creates an extension block declaration + /// + internal abstract SyntaxNode ExtensionBlockDeclaration( + SyntaxNode extensionParameter, + IEnumerable? typeParameters, + IEnumerable members); + /// /// Creates an enum member /// @@ -765,7 +774,7 @@ public SyntaxNode Declaration(ISymbol symbol) modifiers: DeclarationModifiers.From(type), baseType: type.BaseType != null ? TypeExpression(type.BaseType) : null, interfaceTypes: type.Interfaces.Select(TypeExpression), - members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), + members: GetMembersExceptExtensionImplementations(type).Where(CanBeDeclared).Select(Declaration)), TypeKind.Struct => StructDeclaration( type.IsRecord, type.Name, @@ -773,20 +782,20 @@ public SyntaxNode Declaration(ISymbol symbol) accessibility: type.DeclaredAccessibility, modifiers: DeclarationModifiers.From(type), interfaceTypes: type.Interfaces.Select(TypeExpression), - members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), + members: type.GetMembers().SelectAsArray(CanBeDeclared, Declaration)), TypeKind.Interface => InterfaceDeclaration( type.Name, type.TypeParameters.Select(TypeParameter), accessibility: type.DeclaredAccessibility, interfaceTypes: type.Interfaces.Select(TypeExpression), - members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), + members: type.GetMembers().SelectAsArray(CanBeDeclared, Declaration)), TypeKind.Enum => EnumDeclaration( type.Name, underlyingType: type.EnumUnderlyingType is null or { SpecialType: SpecialType.System_Int32 } ? null : TypeExpression(type.EnumUnderlyingType.SpecialType), accessibility: type.DeclaredAccessibility, - members: type.GetMembers().Where(s => s.Kind == SymbolKind.Field).Select(Declaration)), + members: type.GetMembers().SelectAsArray(s => s.Kind == SymbolKind.Field, Declaration)), TypeKind.Delegate => type.GetMembers(WellKnownMemberNames.DelegateInvokeName) is [IMethodSymbol invoke, ..] ? DelegateDeclaration( type.Name, @@ -796,6 +805,10 @@ public SyntaxNode Declaration(ISymbol symbol) accessibility: type.DeclaredAccessibility, modifiers: DeclarationModifiers.From(type)) : null, + TypeKind.Extension when type.ExtensionParameter is { } extensionParameter => ExtensionBlockDeclaration( + ParameterDeclaration(extensionParameter), + typeParameters: type.TypeParameters.Select(TypeParameter), + members: type.GetMembers().Where(CanBeDeclared).Select(Declaration)), _ => null, }; @@ -806,6 +819,43 @@ public SyntaxNode Declaration(ISymbol symbol) } throw new ArgumentException("Symbol cannot be converted to a declaration"); + + static IEnumerable GetMembersExceptExtensionImplementations(INamedTypeSymbol type) + { + var members = type.GetMembers(); + using var _ = PooledHashSet.GetInstance(out var implementationsToHide); + foreach (var nested in type.GetTypeMembers("")) + { + if (nested.IsExtension) + { + foreach (var extensionMember in nested.GetMembers()) + { + if (extensionMember is IMethodSymbol { OriginalDefinition.AssociatedExtensionImplementation: { } toShadow }) + { + implementationsToHide.Add(toShadow); + } + } + } + } + + if (implementationsToHide is null) + { + return members; + } + + using var _2 = ArrayBuilder.GetInstance(out var result); + foreach (var member in members) + { + // Hide implementation methods + if (member is not IMethodSymbol method || + !implementationsToHide.Contains(method.OriginalDefinition)) + { + result.Add(member); + } + } + + return result.ToImmutableAndClear(); + } } private static bool CanBeDeclared(ISymbol symbol) @@ -832,6 +882,7 @@ private static bool CanBeDeclared(ISymbol symbol) { case MethodKind.Constructor: case MethodKind.SharedConstructor: + case MethodKind.UserDefinedOperator: return true; case MethodKind.Ordinary: return method.CanBeReferencedByName; @@ -849,6 +900,8 @@ private static bool CanBeDeclared(ISymbol symbol) case TypeKind.Enum: case TypeKind.Delegate: return type.CanBeReferencedByName; + case TypeKind.Extension: + return true; } break; diff --git a/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs b/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs index 9080f943b6c..10b668b5471 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaEditDistanceWrapper.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api; internal readonly struct PythiaEditDistanceWrapper(string str) : IDisposable { - private readonly EditDistance _underlyingObject = new EditDistance(str); + private readonly EditDistance _underlyingObject = new(str); public double GetEditDistance(string target) => _underlyingObject.GetEditDistance(target); diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs index 040c92bbedb..3e7f4ce3218 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferenceCache.cs @@ -69,6 +69,7 @@ static async Task ComputeCacheAsync(Document document, Cance private ImmutableHashSet? _aliasNameSet; private ImmutableArray _constructorInitializerCache; private ImmutableArray _newKeywordsCache; + private ImmutableArray _constructorDeclarations; private FindReferenceCache( Document document, SourceText text, SemanticModel semanticModel, SemanticModel nullableEnabledSemanticModel, SyntaxNode root, SyntaxTreeIndex syntaxTreeIndex) @@ -223,7 +224,7 @@ ImmutableArray GetConstructorInitializerTokensWorker() { var syntaxFacts = this.SyntaxFacts; using var _ = ArrayBuilder.GetInstance(out var initializers); - foreach (var constructor in syntaxFacts.GetConstructors(this.Root, cancellationToken)) + foreach (var constructor in this.GetConstructorDeclarations(cancellationToken)) { foreach (var token in constructor.DescendantTokens(descendIntoTrivia: false)) { @@ -236,6 +237,47 @@ ImmutableArray GetConstructorInitializerTokensWorker() } } + public ImmutableArray GetConstructorDeclarations(CancellationToken cancellationToken) + { + if (_constructorDeclarations.IsDefault) + { + ImmutableInterlocked.InterlockedInitialize( + ref _constructorDeclarations, + ComputeConstructorDeclarations(cancellationToken)); + } + return _constructorDeclarations; + } + + private ImmutableArray ComputeConstructorDeclarations(CancellationToken cancellationToken) + { + if (this.Root is not ICompilationUnitSyntax) + return []; + + using var _ = ArrayBuilder.GetInstance(out var constructors); + AppendConstructors(this.SyntaxFacts.GetMembersOfCompilationUnit(this.Root)); + return constructors.ToImmutableAndClear(); + + void AppendConstructors(SyntaxList members) + { + foreach (var member in members) + { + cancellationToken.ThrowIfCancellationRequested(); + if (this.SyntaxFacts.IsConstructorDeclaration(member)) + { + constructors.Add(member); + } + else if (this.SyntaxFacts.IsBaseNamespaceDeclaration(member)) + { + AppendConstructors(this.SyntaxFacts.GetMembersOfBaseNamespaceDeclaration(member)); + } + else if (this.SyntaxFacts.IsTypeDeclaration(member)) + { + AppendConstructors(this.SyntaxFacts.GetMembersOfTypeDeclaration(member)); + } + } + } + } + public ImmutableArray GetNewKeywordTokens(CancellationToken cancellationToken) { if (_newKeywordsCache.IsDefault) diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs index 46be6a928a4..574fdca8ab7 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs @@ -28,9 +28,7 @@ protected override bool CanFind(IMethodSymbol symbol) protected override ValueTask> DetermineCascadedSymbolsAsync(IMethodSymbol symbol, Solution solution, FindReferencesSearchOptions options, CancellationToken cancellationToken) { if (symbol.MethodKind is MethodKind.Constructor) - { return new(GetOtherPartsOfPartial(symbol)); - } return new([]); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConstructorInitializerSymbolReferenceFinder.cs similarity index 55% rename from src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs rename to src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConstructorInitializerSymbolReferenceFinder.cs index 817f2e31571..d51a960d908 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConstructorInitializerSymbolReferenceFinder.cs @@ -12,54 +12,48 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders; -internal sealed class ConstructorInitializerSymbolReferenceFinder : AbstractReferenceFinder +/// +/// For finding explicit calls to a constructor via "this(...)" or "base(...)". +/// +internal sealed class ExplicitConstructorInitializerSymbolReferenceFinder + : ExplicitOrImplicitConstructorInitializerSymbolReferenceFinder { - protected override bool CanFind(IMethodSymbol symbol) - => symbol.MethodKind == MethodKind.Constructor; + public static readonly ExplicitConstructorInitializerSymbolReferenceFinder Instance = new(); - protected override Task DetermineDocumentsToSearchAsync( - IMethodSymbol symbol, - HashSet? globalAliases, - Project project, - IImmutableSet? documents, - Action processResult, - TData processResultData, - FindReferencesSearchOptions options, - CancellationToken cancellationToken) + private ExplicitConstructorInitializerSymbolReferenceFinder() + { + } + + protected override bool CheckIndex(Document document, string name, SyntaxTreeIndex index) { - return FindDocumentsAsync(project, documents, static async (document, name, cancellationToken) => + if (index.ContainsExplicitBaseConstructorInitializer) { - var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); + // if we have `partial class C { ... : base(...) }` we have to assume it might be a match, as the base + // type reference might be in a another part of the partial in another file. + if (index.ContainsPartialClass) + return true; - if (index.ContainsBaseConstructorInitializer) - { - // if we have `partial class C { ... : base(...) }` we have to assume it might be a match, as the base - // type reference might be in a another part of the partial in another file. - if (index.ContainsPartialClass) - return true; + // Otherwise, if it doesn't have any partial types, ensure that the base type name is referenced in the + // same file. e.g. `partial class C : B { ... base(...) }`. This allows us to greatly filter down the + // number of matches, presuming that most inheriting types in a project are not themselves partial. + if (index.ProbablyContainsIdentifier(name)) + return true; + } - // Otherwise, if it doesn't have any partial types, ensure that the base type name is referenced in the - // same file. e.g. `partial class C : B { ... base(...) }`. This allows us to greatly filter down the - // number of matches, presuming that most inheriting types in a project are not themselves partial. - if (index.ProbablyContainsIdentifier(name)) - return true; + if (index.ProbablyContainsIdentifier(name)) + { + if (index.ContainsThisConstructorInitializer) + { + return true; } - - if (index.ProbablyContainsIdentifier(name)) + else if (document.Project.Language == LanguageNames.VisualBasic && index.ProbablyContainsIdentifier("New")) { - if (index.ContainsThisConstructorInitializer) - { - return true; - } - else if (document.Project.Language == LanguageNames.VisualBasic && index.ProbablyContainsIdentifier("New")) - { - // "New" can be explicitly accessed in xml doc comments to reference a constructor. - return true; - } + // "New" can be explicitly accessed in xml doc comments to reference a constructor. + return true; } + } - return false; - }, symbol.ContainingType.Name, processResult, processResultData, cancellationToken); + return false; } protected sealed override void FindReferencesInDocument( diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitOrImplicitConstructorInitializerSymbolReferenceFinder.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitOrImplicitConstructorInitializerSymbolReferenceFinder.cs new file mode 100644 index 00000000000..c47ab4f4c8e --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitOrImplicitConstructorInitializerSymbolReferenceFinder.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.FindSymbols.Finders; + +internal abstract class ExplicitOrImplicitConstructorInitializerSymbolReferenceFinder : AbstractReferenceFinder +{ + protected abstract bool CheckIndex(Document document, string name, SyntaxTreeIndex index); + + protected override bool CanFind(IMethodSymbol symbol) + => symbol.MethodKind == MethodKind.Constructor; + + protected sealed override Task DetermineDocumentsToSearchAsync( + IMethodSymbol symbol, + HashSet? globalAliases, + Project project, + IImmutableSet? documents, + Action processResult, + TData processResultData, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + return FindDocumentsAsync(project, documents, static async (document, tuple, cancellationToken) => + { + var (@this, name) = tuple; + var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); + return @this.CheckIndex(document, name, index); + }, (this, symbol.ContainingType.Name), processResult, processResultData, cancellationToken); + } +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ImplicitConstructorInitializerSymbolReferenceFinder.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ImplicitConstructorInitializerSymbolReferenceFinder.cs new file mode 100644 index 00000000000..0f31bd78057 --- /dev/null +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ImplicitConstructorInitializerSymbolReferenceFinder.cs @@ -0,0 +1,84 @@ +// 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.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.FindSymbols.Finders; + +/// +/// For finding references to implicit constructor initializers (i.e. the implicit "base()" call when non is +/// explicitly supplied). +/// +internal sealed class ImplicitConstructorInitializerSymbolReferenceFinder : ExplicitOrImplicitConstructorInitializerSymbolReferenceFinder +{ + public static readonly ImplicitConstructorInitializerSymbolReferenceFinder Instance = new(); + + private ImplicitConstructorInitializerSymbolReferenceFinder() + { + } + + /// + /// Only use this finder if the constructor is one that can be called without parameters (and thus a base class + /// could be calling it implicitly). + /// + protected override bool CanFind(IMethodSymbol symbol) + => base.CanFind(symbol) && symbol.Parameters.All(p => p.IsOptional || p.IsParams); + + protected override bool CheckIndex(Document document, string name, SyntaxTreeIndex index) + { + if (index.ContainsImplicitBaseConstructorInitializer) + { + // if we have `partial class C { public C() { } }` we have to assume it might be a match, as the base + // type reference might be in a another part of the partial in another file. + if (index.ContainsPartialClass) + return true; + + // Otherwise, if it doesn't have any partial types, ensure that the base type name is referenced in the + // same file. e.g. `class C : B { public C() { } }`. This allows us to greatly filter down the + // number of matches, presuming that most inheriting types in a project are not themselves partial. + if (index.ProbablyContainsIdentifier(name)) + return true; + } + + return false; + } + + protected sealed override void FindReferencesInDocument( + IMethodSymbol methodSymbol, + FindReferencesDocumentState state, + Action processResult, + TData processResultData, + FindReferencesSearchOptions options, + CancellationToken cancellationToken) + { + var syntaxFacts = state.SyntaxFacts; + var constructorNodes = state.Cache.GetConstructorDeclarations(cancellationToken); + foreach (var constructorNode in constructorNodes) + { + if (!syntaxFacts.HasImplicitBaseConstructorInitializer(constructorNode)) + continue; + + // Looks like a suitable match. A constructor with an implicit base constructor initializer. See if the + // constructor's base type is the type that contains the constructor we're looking for. + if (state.SemanticModel.GetDeclaredSymbol(constructorNode, cancellationToken) is IMethodSymbol constructor && + SymbolFinder.OriginalSymbolsMatch(state.Solution, methodSymbol.ContainingType, constructor.ContainingType.BaseType)) + { + processResult(new(constructorNode, new( + state.Document, + alias: null, + constructor.Locations.First(loc => loc.SourceTree == constructorNode.SyntaxTree && constructorNode.Span.IntersectsWith(loc.SourceSpan)), + isImplicit: true, + new(valueUsageInfoOpt: null, TypeOrNamespaceUsageInfo.ObjectCreation), + additionalProperties: [], + CandidateReason.None)), + processResultData); + } + } + } +} diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs index 863c60a0b68..8f4b9968b49 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ReferenceFinders.cs @@ -8,9 +8,8 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders; internal static class ReferenceFinders { - // Rename does not need to include base/this constructor initializer calls - public static readonly ImmutableArray DefaultRenameReferenceFinders = - [ + // Rename does not need to include base/this constructor initializer calls (explicit or implicit). + public static readonly ImmutableArray DefaultRenameReferenceFinders = [ ConstructorSymbolReferenceFinder.Instance, PropertySymbolReferenceFinder.Instance, new DestructorSymbolReferenceFinder(), @@ -36,5 +35,8 @@ internal static class ReferenceFinders /// /// The list of common reference finders. /// - internal static readonly ImmutableArray DefaultReferenceFinders = [.. DefaultRenameReferenceFinders, new ConstructorInitializerSymbolReferenceFinder()]; + internal static readonly ImmutableArray DefaultReferenceFinders = [ + .. DefaultRenameReferenceFinders, + ExplicitConstructorInitializerSymbolReferenceFinder.Instance, + ImplicitConstructorInitializerSymbolReferenceFinder.Instance]; } diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 6ea1a68682c..96cdb613307 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -23,7 +23,7 @@ internal abstract partial class AbstractSyntaxIndex /// that we will not try to read previously cached data from a prior version of roslyn with a different format and /// will instead regenerate all the indices with the new format. /// - private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("48"); + private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("49"); /// /// Cache of ParseOptions to a checksum for the contained diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index bd74857164f..dc2b7a64b83 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -24,7 +24,8 @@ public ContextInfo( bool containsUsingStatement, bool containsQueryExpression, bool containsThisConstructorInitializer, - bool containsBaseConstructorInitializer, + bool containsExplicitBaseConstructorInitializer, + bool containsImplicitBaseConstructorInitializer, bool containsExplicitOrImplicitElementAccessExpression, bool containsIndexerMemberCref, bool containsDeconstruction, @@ -46,7 +47,8 @@ public ContextInfo( containsUsingStatement, containsQueryExpression, containsThisConstructorInitializer, - containsBaseConstructorInitializer, + containsExplicitBaseConstructorInitializer, + containsImplicitBaseConstructorInitializer, containsExplicitOrImplicitElementAccessExpression, containsIndexerMemberCref, containsDeconstruction, @@ -77,7 +79,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsUsingStatement, bool containsQueryExpression, bool containsThisConstructorInitializer, - bool containsBaseConstructorInitializer, + bool containsExplicitBaseConstructorInitializer, + bool containsImplicitBaseConstructorInitializer, bool containsExplicitOrImplicitElementAccessExpression, bool containsIndexerMemberCref, bool containsDeconstruction, @@ -100,7 +103,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes |= containsUsingStatement ? ContainingNodes.ContainsUsingStatement : 0; containingNodes |= containsQueryExpression ? ContainingNodes.ContainsQueryExpression : 0; containingNodes |= containsThisConstructorInitializer ? ContainingNodes.ContainsThisConstructorInitializer : 0; - containingNodes |= containsBaseConstructorInitializer ? ContainingNodes.ContainsBaseConstructorInitializer : 0; + containingNodes |= containsExplicitBaseConstructorInitializer ? ContainingNodes.ContainsExplicitBaseConstructorInitializer : 0; + containingNodes |= containsImplicitBaseConstructorInitializer ? ContainingNodes.ContainsImplicitBaseConstructorInitializer : 0; containingNodes |= containsExplicitOrImplicitElementAccessExpression ? ContainingNodes.ContainsExplicitOrImplicitElementAccessExpression : 0; containingNodes |= containsIndexerMemberCref ? ContainingNodes.ContainsIndexerMemberCref : 0; containingNodes |= containsDeconstruction ? ContainingNodes.ContainsDeconstruction : 0; @@ -152,8 +156,11 @@ public bool ContainsQueryExpression public bool ContainsThisConstructorInitializer => (_containingNodes & ContainingNodes.ContainsThisConstructorInitializer) == ContainingNodes.ContainsThisConstructorInitializer; - public bool ContainsBaseConstructorInitializer - => (_containingNodes & ContainingNodes.ContainsBaseConstructorInitializer) == ContainingNodes.ContainsBaseConstructorInitializer; + public bool ContainsExplicitBaseConstructorInitializer + => (_containingNodes & ContainingNodes.ContainsExplicitBaseConstructorInitializer) == ContainingNodes.ContainsExplicitBaseConstructorInitializer; + + public bool ContainsImplicitBaseConstructorInitializer + => (_containingNodes & ContainingNodes.ContainsExplicitBaseConstructorInitializer) == ContainingNodes.ContainsExplicitBaseConstructorInitializer; public bool ContainsExplicitOrImplicitElementAccessExpression => (_containingNodes & ContainingNodes.ContainsExplicitOrImplicitElementAccessExpression) == ContainingNodes.ContainsExplicitOrImplicitElementAccessExpression; @@ -218,21 +225,22 @@ private enum ContainingNodes ContainsUsingStatement = 1 << 2, ContainsQueryExpression = 1 << 3, ContainsThisConstructorInitializer = 1 << 4, - ContainsBaseConstructorInitializer = 1 << 5, - ContainsExplicitOrImplicitElementAccessExpression = 1 << 6, - ContainsIndexerMemberCref = 1 << 7, - ContainsDeconstruction = 1 << 8, - ContainsAwait = 1 << 9, - ContainsTupleExpressionOrTupleType = 1 << 10, - ContainsImplicitObjectCreation = 1 << 11, - ContainsGlobalSuppressMessageAttribute = 1 << 12, - ContainsConversion = 1 << 13, - ContainsGlobalKeyword = 1 << 14, - ContainsCollectionInitializer = 1 << 15, - ContainsAttribute = 1 << 16, - ContainsDirective = 1 << 17, - ContainsPrimaryConstructorBaseType = 1 << 18, - ContainsPartialClass = 1 << 19, + ContainsImplicitBaseConstructorInitializer = 1 << 5, + ContainsExplicitBaseConstructorInitializer = 1 << 6, + ContainsExplicitOrImplicitElementAccessExpression = 1 << 7, + ContainsIndexerMemberCref = 1 << 8, + ContainsDeconstruction = 1 << 9, + ContainsAwait = 1 << 10, + ContainsTupleExpressionOrTupleType = 1 << 11, + ContainsImplicitObjectCreation = 1 << 12, + ContainsGlobalSuppressMessageAttribute = 1 << 13, + ContainsConversion = 1 << 14, + ContainsGlobalKeyword = 1 << 15, + ContainsCollectionInitializer = 1 << 16, + ContainsAttribute = 1 << 17, + ContainsDirective = 1 << 18, + ContainsPrimaryConstructorBaseType = 1 << 19, + ContainsPartialClass = 1 << 20, } } } diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 6299181a4f5..bf7abc8e417 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -61,7 +61,8 @@ private static SyntaxTreeIndex CreateIndex( var containsUsingStatement = false; var containsQueryExpression = false; var containsThisConstructorInitializer = false; - var containsBaseConstructorInitializer = false; + var containsExplicitBaseConstructorInitializer = false; + var containsImplicitBaseConstructorInitializer = false; var containsElementAccess = false; var containsIndexerMemberCref = false; var containsDeconstruction = false; @@ -109,6 +110,8 @@ private static SyntaxTreeIndex CreateIndex( containsAttribute = containsAttribute || syntaxFacts.IsAttribute(node); containsPrimaryConstructorBaseType = containsPrimaryConstructorBaseType || syntaxFacts.IsPrimaryConstructorBaseType(node); containsPartialClass = containsPartialClass || IsPartialClass(node); + containsImplicitBaseConstructorInitializer = containsImplicitBaseConstructorInitializer || + (syntaxFacts.IsConstructorDeclaration(node) && syntaxFacts.HasImplicitBaseConstructorInitializer(node)); TryAddAliasInfo(syntaxFacts, ref aliasInfo, node); @@ -120,7 +123,7 @@ private static SyntaxTreeIndex CreateIndex( var token = (SyntaxToken)current; containsThisConstructorInitializer = containsThisConstructorInitializer || syntaxFacts.IsThisConstructorInitializer(token); - containsBaseConstructorInitializer = containsBaseConstructorInitializer || syntaxFacts.IsBaseConstructorInitializer(token); + containsExplicitBaseConstructorInitializer = containsExplicitBaseConstructorInitializer || syntaxFacts.IsBaseConstructorInitializer(token); containsGlobalKeyword = containsGlobalKeyword || syntaxFacts.IsGlobalNamespaceKeyword(token); if (syntaxFacts.IsIdentifier(token)) @@ -190,7 +193,8 @@ private static SyntaxTreeIndex CreateIndex( containsUsingStatement, containsQueryExpression, containsThisConstructorInitializer, - containsBaseConstructorInitializer, + containsExplicitBaseConstructorInitializer, + containsImplicitBaseConstructorInitializer, containsElementAccess, containsIndexerMemberCref, containsDeconstruction, diff --git a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index 7ff47aee566..9cfd64b062e 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -23,13 +23,14 @@ internal sealed partial class SyntaxTreeIndex public bool ProbablyContainsInt64Value(long value) => _literalInfo.ProbablyContainsInt64Value(value); public bool ContainsAwait => _contextInfo.ContainsAwait; - public bool ContainsBaseConstructorInitializer => _contextInfo.ContainsBaseConstructorInitializer; public bool ContainsConversion => _contextInfo.ContainsConversion; public bool ContainsDeconstruction => _contextInfo.ContainsDeconstruction; + public bool ContainsExplicitBaseConstructorInitializer => _contextInfo.ContainsExplicitBaseConstructorInitializer; public bool ContainsExplicitOrImplicitElementAccessExpression => _contextInfo.ContainsExplicitOrImplicitElementAccessExpression; public bool ContainsForEachStatement => _contextInfo.ContainsForEachStatement; public bool ContainsGlobalKeyword => _contextInfo.ContainsGlobalKeyword; public bool ContainsGlobalSuppressMessageAttribute => _contextInfo.ContainsGlobalSuppressMessageAttribute; + public bool ContainsImplicitBaseConstructorInitializer => _contextInfo.ContainsImplicitBaseConstructorInitializer; public bool ContainsImplicitObjectCreation => _contextInfo.ContainsImplicitObjectCreation; public bool ContainsIndexerMemberCref => _contextInfo.ContainsIndexerMemberCref; public bool ContainsLockStatement => _contextInfo.ContainsLockStatement; diff --git a/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs b/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs index c485e690719..45dc35e5dc1 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs @@ -20,7 +20,7 @@ private struct PatternSegment(string text, bool allowFuzzyMatching) : IDisposabl // Information about the entire piece of text between the dots. For example, if the // text between the dots is 'Get-Keyword', then TotalTextChunk.Text will be 'Get-Keyword' and // TotalTextChunk.CharacterSpans will correspond to 'G', 'et', 'K' and 'eyword'. - public TextChunk TotalTextChunk = new TextChunk(text, allowFuzzyMatching); + public TextChunk TotalTextChunk = new(text, allowFuzzyMatching); // Information about the subwords compromising the total word. For example, if the // text between the dots is 'Get-Keyword', then the subwords will be 'Get' and 'Keyword' diff --git a/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index a0961d4c58c..351a1e85e63 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/roslyn/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1,4 +1,24 @@ *REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithModifiers(Microsoft.CodeAnalysis.SyntaxNode declaration, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers) -> Microsoft.CodeAnalysis.SyntaxNode +abstract Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.GetRefactoringAsync(Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext refactorAllContext) -> System.Threading.Tasks.Task +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.CancellationToken.get -> System.Threading.CancellationToken +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.CodeActionEquivalenceKey.get -> string +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.CodeRefactoringProvider.get -> Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Document.get -> Microsoft.CodeAnalysis.Document +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.GetRefactorAllSpansAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>>> +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Progress.get -> System.IProgress +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Project.get -> Microsoft.CodeAnalysis.Project +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Scope.get -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllContext.Solution.get -> Microsoft.CodeAnalysis.Solution +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.RefactorAllProvider() -> void +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.ContainingMember = 4 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.ContainingType = 5 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Custom = 3 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Document = 0 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Project = 1 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope +Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope.Solution = 2 -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllScope Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithModifiers(Microsoft.CodeAnalysis.SyntaxNode declaration, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers) -> Microsoft.CodeAnalysis.SyntaxNode Microsoft.CodeAnalysis.Editing.OperatorKind.AdditionAssignment = 27 -> Microsoft.CodeAnalysis.Editing.OperatorKind Microsoft.CodeAnalysis.Editing.OperatorKind.BitwiseAndAssignment = 33 -> Microsoft.CodeAnalysis.Editing.OperatorKind @@ -31,7 +51,11 @@ Microsoft.CodeAnalysis.WorkspaceEventRegistration.Dispose() -> void override Microsoft.CodeAnalysis.WorkspaceEventOptions.Equals(object obj) -> bool override Microsoft.CodeAnalysis.WorkspaceEventOptions.GetHashCode() -> int override Microsoft.CodeAnalysis.WorkspaceEventOptions.ToString() -> string +static Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.Create(System.Func>, System.Threading.Tasks.Task> refactorAllAsync) -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider +static Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.Create(System.Func>, System.Threading.Tasks.Task> refactorAllAsync, System.Collections.Immutable.ImmutableArray supportedRefactorAllScopes) -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider static Microsoft.CodeAnalysis.WorkspaceEventOptions.operator !=(Microsoft.CodeAnalysis.WorkspaceEventOptions left, Microsoft.CodeAnalysis.WorkspaceEventOptions right) -> bool static Microsoft.CodeAnalysis.WorkspaceEventOptions.operator ==(Microsoft.CodeAnalysis.WorkspaceEventOptions left, Microsoft.CodeAnalysis.WorkspaceEventOptions right) -> bool static readonly Microsoft.CodeAnalysis.WorkspaceEventOptions.DefaultOptions -> Microsoft.CodeAnalysis.WorkspaceEventOptions -static readonly Microsoft.CodeAnalysis.WorkspaceEventOptions.RequiresMainThreadOptions -> Microsoft.CodeAnalysis.WorkspaceEventOptions \ No newline at end of file +static readonly Microsoft.CodeAnalysis.WorkspaceEventOptions.RequiresMainThreadOptions -> Microsoft.CodeAnalysis.WorkspaceEventOptions +virtual Microsoft.CodeAnalysis.CodeRefactorings.CodeRefactoringProvider.GetRefactorAllProvider() -> Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider +virtual Microsoft.CodeAnalysis.CodeRefactorings.RefactorAllProvider.GetSupportedRefactorAllScopes() -> System.Collections.Generic.IEnumerable \ No newline at end of file diff --git a/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs b/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs index 7af875c8cac..21dc0c0b02f 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictingIdentifierTracker.cs @@ -13,7 +13,7 @@ internal sealed class ConflictingIdentifierTracker(SyntaxToken tokenBeingRenamed /// current identifier tokens that are declaring variables. This should only ever be updated /// via the AddIdentifier and RemoveIdentifier helpers. /// - private readonly Dictionary> _currentIdentifiersInScope = new Dictionary>(identifierComparer); + private readonly Dictionary> _currentIdentifiersInScope = new(identifierComparer); private readonly HashSet _conflictingTokensToReport = []; public IEnumerable ConflictingTokens => _conflictingTokensToReport; diff --git a/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index 677d048ee36..d207e3a6894 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -88,7 +88,7 @@ internal static IEnumerable GetDocumentsAffectedByRename(ISymbol symbo } else { - var documentsOfRenameSymbolDeclaration = symbol.Locations.Where(l => l.IsInSource).Select(l => solution.GetRequiredDocument(l.SourceTree!)); + var documentsOfRenameSymbolDeclaration = symbol.Locations.SelectAsArray(l => l.IsInSource, l => solution.GetRequiredDocument(l.SourceTree!)); var projectIdsOfRenameSymbolDeclaration = documentsOfRenameSymbolDeclaration.SelectMany(d => d.GetLinkedDocumentIds()) .Concat(documentsOfRenameSymbolDeclaration.First().Id) diff --git a/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs b/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs index e4376a18de6..967d624f9dc 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs @@ -20,10 +20,10 @@ internal static class ILanguageMetadataExtensions return services.Where(s => s.Metadata.Language == languageName).Select(s => s.Value).FirstOrDefault(); } - public static IEnumerable FilterToSpecificLanguage(this IEnumerable> services, string languageName) + public static ImmutableArray FilterToSpecificLanguage(this IEnumerable> services, string languageName) where TMetadata : ILanguageMetadata { - return services.Where(s => s.Metadata.Language == languageName).Select(s => s.Value); + return services.SelectAsArray(s => s.Metadata.Language == languageName, s => s.Value); } public static ImmutableDictionary>> ToPerLanguageMap(this IEnumerable> services) diff --git a/src/roslyn/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs b/src/roslyn/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs index 70b798c095e..9515626c858 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Utilities/Documentation/XmlDocumentationProvider.cs @@ -11,7 +11,6 @@ using System.IO; using System.Threading; using System.Xml; -using System.Xml.Linq; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -55,13 +54,6 @@ public static XmlDocumentationProvider CreateFromFile(string xmlDocCommentFilePa return new FileBasedXmlDocumentationProvider(xmlDocCommentFilePath); } - private XDocument GetXDocument(CancellationToken cancellationToken) - { - using var stream = GetSourceStream(cancellationToken); - using var xmlReader = XmlReader.Create(stream, s_xmlSettings); - return XDocument.Load(xmlReader); - } - protected override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default) { if (_docComments == null) @@ -70,16 +62,26 @@ protected override string GetDocumentationForSymbol(string documentationMemberID { try { - var comments = new Dictionary(); - - var doc = GetXDocument(cancellationToken); - foreach (var e in doc.Descendants("member")) + using (var stream = GetSourceStream(cancellationToken)) + using (var xmlReader = XmlReader.Create(stream, s_xmlSettings)) { - if (e.Attribute("name") != null) - comments[e.Attribute("name").Value] = e.ToString(); + var comments = new Dictionary(); + while (xmlReader.Read()) + { + if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "member") + { + var name = xmlReader.GetAttribute("name"); + if (name != null) + { + // Read the entire element as a string + var memberXml = xmlReader.ReadOuterXml(); + comments[name] = memberXml; + } + } + } + + _docComments = comments; } - - _docComments = comments; } catch (Exception) { diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs index 3179339c11e..5551dac4b39 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Documentation/DocumentationProviderServiceFactory.cs @@ -24,8 +24,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) internal sealed class DocumentationProviderService : IDocumentationProviderService { - private readonly ConcurrentDictionary _assemblyPathToDocumentationProviderMap = - new(); + private readonly ConcurrentDictionary _assemblyPathToDocumentationProviderMap = new(); public DocumentationProvider GetDocumentationProvider(string assemblyPath) { diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs index 21122abcb22..9a2fec57b15 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Host/Metadata/MetadataServiceFactory.cs @@ -24,7 +24,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) private sealed class DefaultMetadataService(IDocumentationProviderService documentationService) : IMetadataService { - private readonly MetadataReferenceCache _metadataCache = new MetadataReferenceCache((path, properties) => + private readonly MetadataReferenceCache _metadataCache = new((path, properties) => MetadataReference.CreateFromFile(path, properties, documentationService.GetDocumentationProvider(path))); public PortableExecutableReference GetReference(string resolvedPath, MetadataReferenceProperties properties) diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index 32b4f480eea..07508b3cc54 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -44,6 +44,9 @@ public static Checksum Create(IEnumerable values) } public static Checksum Create(ImmutableArray values) + => Create(ImmutableCollectionsMarshal.AsArray(values).AsSpan()); + + public static Checksum Create(ReadOnlySpan values) { using var pooledHash = s_incrementalHashPool.GetPooledObject(); @@ -159,11 +162,12 @@ public static Checksum Create(ImmutableArray checksums) } public static Checksum Create(ImmutableArray bytes) - { - var source = ImmutableCollectionsMarshal.AsArray(bytes).AsSpan(); + => Create(ImmutableCollectionsMarshal.AsArray(bytes).AsSpan()); + public static Checksum Create(ReadOnlySpan bytes) + { Span destination = stackalloc byte[XXHash128SizeBytes]; - XxHash128.Hash(source, destination); + XxHash128.Hash(bytes, destination); return From(destination); } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs index e6d1c1f92ba..7754b7924a1 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.RegularCompilationTracker.cs @@ -33,7 +33,7 @@ private sealed partial class RegularCompilationTracker : ICompilationTracker private static readonly Func s_logBuildCompilationAsync = state => string.Join(",", state.AssemblyName, state.DocumentStates.Count); - private static readonly CancellableLazy s_lazyNullCompilation = new CancellableLazy((Compilation?)null); + private static readonly CancellableLazy s_lazyNullCompilation = new((Compilation?)null); public ProjectState ProjectState { get; } diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 1f9ca11562d..5ce65e7a6c8 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -68,7 +68,7 @@ internal sealed partial class SolutionState private readonly Lazy _lazyAnalyzers; // Mapping from file path to the set of documents that are related to it. - private readonly ConcurrentDictionary> _lazyFilePathToRelatedDocumentIds = new ConcurrentDictionary>(FilePathComparer); + private readonly ConcurrentDictionary> _lazyFilePathToRelatedDocumentIds = new(FilePathComparer); private SolutionState( string? workspaceKind, diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs index e991d2baddf..846344ca0ee 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Security.Cryptography; using System.Runtime.Serialization; using System.Text; using Microsoft.CodeAnalysis.Diagnostics; @@ -41,31 +42,30 @@ public static SourceGeneratedDocumentIdentity Generate(ProjectId projectId, stri // ensure we don't have collisions. var generatorIdentity = SourceGeneratorIdentity.Create(generator, analyzerReference); - // Combine the strings together; we'll use Encoding.Unicode since that'll match the underlying format; this can be made much - // faster once we're on .NET Core since we could directly treat the strings as ReadOnlySpan. - var projectIdBytes = projectId.Id.ToByteArray(); - // The assembly path should exist in any normal scenario; the hashing of the name only would apply if the user loaded a // dynamic assembly they produced at runtime and passed us that via a custom AnalyzerReference. var assemblyNameToHash = generatorIdentity.AssemblyPath ?? generatorIdentity.AssemblyName; - using var _ = ArrayBuilder.GetInstance(capacity: (assemblyNameToHash.Length + 1 + generatorIdentity.TypeName.Length + 1 + hintName.Length) * 2 + projectIdBytes.Length, out var hashInput); - hashInput.AddRange(projectIdBytes); - - // Add a null to separate the generator name and hint name; since this is effectively a joining of UTF-16 bytes - // we'll use a UTF-16 null just to make sure there's absolutely no risk of collision. - hashInput.AddRange(Encoding.Unicode.GetBytes(assemblyNameToHash)); - hashInput.AddRange(0, 0); - hashInput.AddRange(Encoding.Unicode.GetBytes(generatorIdentity.TypeName)); - hashInput.AddRange(0, 0); - hashInput.AddRange(Encoding.Unicode.GetBytes(hintName)); - - // The particular choice of crypto algorithm here is arbitrary and can be always changed as necessary. The only requirement - // is it must be collision resistant, and provide enough bits to fill a GUID. - using var crytpoAlgorithm = System.Security.Cryptography.SHA256.Create(); - var hash = crytpoAlgorithm.ComputeHash(hashInput.ToArray()); - Array.Resize(ref hash, 16); - var guid = new Guid(hash); +#if NET + Span bytesToChecksum = stackalloc byte[16]; + projectId.Id.TryWriteBytes(bytesToChecksum); +#else + var bytesToChecksum = projectId.Id.ToByteArray().AsSpan(); +#endif + + ReadOnlySpan stringsToChecksum = [assemblyNameToHash, generatorIdentity.TypeName, hintName]; + var stringChecksum = Checksum.Create(stringsToChecksum); + var byteChecksum = Checksum.Create(bytesToChecksum); + var compositeChecksum = Checksum.Create(stringChecksum, byteChecksum); + + Span checksumAsBytes = stackalloc byte[16]; + compositeChecksum.WriteTo(checksumAsBytes); + +#if NET + var guid = new Guid(checksumAsBytes); +#else + var guid = new Guid(checksumAsBytes.ToArray()); +#endif var documentId = DocumentId.CreateFromSerialized(projectId, guid, isSourceGenerated: true, hintName); diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs index 28ebd8688b8..900d8f599c0 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/WorkspaceEventMap.cs @@ -66,10 +66,10 @@ public sealed class EventHandlerSet(ImmutableArray registries) private readonly ImmutableArray _registries = registries; public static EventHandlerSet Create(WorkspaceEventHandlerAndOptions handlerAndOptions) - => new EventHandlerSet([new Registry(handlerAndOptions)]); + => new([new Registry(handlerAndOptions)]); public EventHandlerSet AddHandler(WorkspaceEventHandlerAndOptions handlerAndOptions) - => new EventHandlerSet(_registries.Add(new Registry(handlerAndOptions))); + => new(_registries.Add(new Registry(handlerAndOptions))); public EventHandlerSet RemoveHandler(WorkspaceEventHandlerAndOptions handlerAndOptions) { diff --git a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs index 732c7325ae0..a3a47ce9e9c 100644 --- a/src/roslyn/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs +++ b/src/roslyn/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs @@ -189,7 +189,7 @@ private EventHandlerSet GetEventHandlers(WorkspaceEventType eventType) return _eventMap.GetEventHandlerSet(eventType); } - private void EnsureEventListeners() + private protected void EnsureEventListeners() { // Cache this service so it doesn't need to be retrieved from MEF during disposal. _workspaceEventListenerService ??= this.Services.GetService(); diff --git a/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs b/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs index 1252e90ead3..b43aa84cc4b 100644 --- a/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/BatchFixAllProviderTests.cs @@ -44,7 +44,7 @@ class TestClass { private sealed class LiteralZeroAnalyzer : DiagnosticAnalyzer { internal static readonly DiagnosticDescriptor Descriptor = - new DiagnosticDescriptor("LiteralZero", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + new("LiteralZero", "title", "message", "category", DiagnosticSeverity.Warning, isEnabledByDefault: true); public override ImmutableArray SupportedDiagnostics => [Descriptor]; diff --git a/src/roslyn/src/Workspaces/CoreTest/ChecksumTests.cs b/src/roslyn/src/Workspaces/CoreTest/ChecksumTests.cs index 3e0b08b288e..b43458f2fa5 100644 --- a/src/roslyn/src/Workspaces/CoreTest/ChecksumTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/ChecksumTests.cs @@ -154,8 +154,8 @@ public void ValidateChecksumFromSpanSameAsChecksumFromBytes10() [Fact] public void StringArraysProduceDifferentResultsThanConcatenation() { - var checksum1 = Checksum.Create(["goo", "bar"]); - var checksum2 = Checksum.Create(["go", "obar"]); + var checksum1 = Checksum.Create(ImmutableArray.Create("goo", "bar")); + var checksum2 = Checksum.Create(ImmutableArray.Create("go", "obar")); var checksum3 = Checksum.Create("goobar"); Assert.NotEqual(checksum1, checksum2); Assert.NotEqual(checksum2, checksum3); @@ -175,9 +175,9 @@ public void DoNotProduceNullChecksum() Assert.NotEqual(Checksum.Null, Checksum.Create(ImmutableArray.Empty)); Assert.NotEqual(Checksum.Null, Checksum.Create(ImmutableArray.Empty)); - Assert.NotEqual(Checksum.Null, Checksum.Create([""])); - Assert.NotEqual(Checksum.Null, Checksum.Create(["\0"])); - Assert.NotEqual(Checksum.Null, Checksum.Create(new string?[] { null })); + Assert.NotEqual(Checksum.Null, Checksum.Create(ImmutableArray.Create(""))); + Assert.NotEqual(Checksum.Null, Checksum.Create(ImmutableArray.Create("\0"))); + Assert.NotEqual(Checksum.Null, Checksum.Create(new string?[] { null }.AsEnumerable())); Assert.NotEqual(Checksum.Null, Checksum.Create(new MemoryStream())); Assert.NotEqual(Checksum.Null, Checksum.Create(stackalloc Checksum[] { Checksum.Null })); Assert.NotEqual(Checksum.Null, Checksum.Create(ImmutableArray.Create(Checksum.Null))); diff --git a/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs b/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs index b299d7dbdea..5675cf36f18 100644 --- a/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Differencing.UnitTests; public sealed class LongestCommonSubsequenceTests { - private readonly LongestCommonSubsequenceString lcs = new LongestCommonSubsequenceString(); + private readonly LongestCommonSubsequenceString lcs = new(); private sealed class LongestCommonSubsequenceString : LongestCommonSubsequence { diff --git a/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs b/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs index aec9f90c374..9265272ae2d 100644 --- a/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs +++ b/src/roslyn/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Differencing.UnitTests; public sealed class TestTreeComparer : TreeComparer { - public static readonly TestTreeComparer Instance = new TestTreeComparer(); + public static readonly TestTreeComparer Instance = new(); private TestTreeComparer() { @@ -50,7 +50,7 @@ protected internal override int GetLabel(TestNode node) => node.Label; protected internal override TextSpan GetSpan(TestNode node) - => new TextSpan(0, 10); + => new(0, 10); protected internal override int TiedToAncestor(int label) => 0; diff --git a/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs b/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs index 0acfee41730..3963f7255e1 100644 --- a/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs @@ -33,7 +33,7 @@ private void VerifySyntax(SyntaxNode node, string expectedText) where T } private SyntaxEditor GetEditor(SyntaxNode root) - => new SyntaxEditor(root, EmptyWorkspace.Services.SolutionServices); + => new(root, EmptyWorkspace.Services.SolutionServices); [Fact] public void TestReplaceNode() diff --git a/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs b/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs index 5f4b181d57b..a5cd9493725 100644 --- a/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs +++ b/src/roslyn/src/Workspaces/CoreTest/ExtensionOrdererTests.cs @@ -206,7 +206,7 @@ public void TestCycle8() #region Helpers private static Lazy CreateExtension(string? name = null, IEnumerable? before = null, IEnumerable? after = null) - => new Lazy(new OrderableMetadata(name, before: before, after: after)); + => new(new OrderableMetadata(name, before: before, after: after)); private static IEnumerable GetNames(IEnumerable> actual) => actual.Select(i => i.Metadata.Name); diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/AsynchronousOperationListenerExtensions.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/AsynchronousOperationListenerExtensions.cs index dad1492c19b..1275420bcc1 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/AsynchronousOperationListenerExtensions.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/AsynchronousOperationListenerExtensions.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.TestHooks; -namespace Roslyn.Test.Utilities; +namespace Microsoft.CodeAnalysis.Test.Utilities; -public static class AsynchronousOperationListenerExtensions +internal static class AsynchronousOperationListenerExtensions { internal static async Task WaitAllDispatcherOperationAndTasksAsync(this IAsynchronousOperationListenerProvider provider, Workspace? workspace, params string[] featureNames) { diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs index 1bdc55adfbc..b2b72bf08cf 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/ExportProviderCache.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.UnitTests.Remote; using Microsoft.VisualStudio.Composition; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Test.Utilities; @@ -21,11 +20,11 @@ public static class ExportProviderCache { private static readonly PartDiscovery s_partDiscovery = CreatePartDiscovery(Resolver.DefaultInstance); - private static readonly TestComposition s_defaultHostExportProviderComposition = TestComposition.Empty - .AddAssemblies(MefHostServices.DefaultAssemblies) + private static readonly TestComposition s_defaultHostExportProviderComposition = TestComposition.Default .AddParts(typeof(TestSerializerService.Factory)); - private static readonly Scope _localCompositionScope = new Scope("local"); - private static readonly Scope _remoteCompositionScope = new Scope("remote"); + + private static readonly Scope _localCompositionScope = new("local"); + private static readonly Scope _remoteCompositionScope = new("remote"); internal static bool Enabled { get; private set; } diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs index 4d96f5edfa1..3cffb70fc38 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/TestComposition.cs @@ -21,7 +21,8 @@ namespace Microsoft.CodeAnalysis.Test.Utilities; /// public sealed class TestComposition { - public static readonly TestComposition Empty = new TestComposition([], [], []); + public static readonly TestComposition Empty = new([], [], []); + public static readonly TestComposition Default = Empty.AddAssemblies(MefHostServices.DefaultAssemblies); private static readonly Dictionary s_factoryCache = []; @@ -88,7 +89,10 @@ private TestComposition(ImmutableHashSet assemblies, ImmutableHashSet< /// depending on what layer the composition is for. Editor Features and VS layers use VS MEF composition while anything else uses System.Composition. /// public HostServices GetHostServices() - => VisualStudioMefHostServices.Create(ExportProviderFactory.CreateExportProvider()); + => GetHostServices(out _); + + public HostServices GetHostServices(out ExportProvider exportProvider) + => VisualStudioMefHostServices.Create(exportProvider = ExportProviderFactory.CreateExportProvider()); /// /// VS MEF . diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/UseExportProviderAttribute.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/UseExportProviderAttribute.cs index 3152fb5510a..8974916709a 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/UseExportProviderAttribute.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/MEF/UseExportProviderAttribute.cs @@ -168,7 +168,7 @@ private static void DisposeExportProvider(ExportProvider? exportProvider) var exceptions = testErrorHandler.Exceptions; if (exceptions.Count > 0) { - throw new AggregateException("Tests threw unexpected exceptions", exceptions); + throw new AggregateException(exceptions); } } } diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj b/src/roslyn/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj index 91d98f244f9..f03767ef6fa 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj @@ -9,10 +9,13 @@ false true true + + + true + This is an internal package for testing. Not meant for external or production uses. The API can and will break at our discretion. - @@ -22,6 +25,7 @@ + diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs index 028b36ae741..a14a0e16cde 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.UnitTests; internal static class OptionsTestHelpers { - public static readonly Option CustomPublicOption = new Option("My Feature", "My Option", defaultValue: true); + public static readonly Option CustomPublicOption = new("My Feature", "My Option", defaultValue: true); // all public options and their non-default values: public static readonly ImmutableArray<(IOption, object)> PublicCustomOptionsWithNonDefaultValues = [(CustomPublicOption, false)]; diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs index aa14c3c3fee..ac61e859841 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestHostProject`1.cs @@ -263,7 +263,7 @@ public ProjectInfo ToProjectInfo() isSubmission: IsSubmission), CompilationOptions, ParseOptions, - documents: Documents.Where(d => !d.IsSourceGenerated).Select(d => d.ToDocumentInfo()), + documents: Documents.SelectAsArray(d => !d.IsSourceGenerated, d => d.ToDocumentInfo()), ProjectReferences, MetadataReferences, AnalyzerReferences, diff --git a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs index 444cb4e6639..4453b5c4481 100644 --- a/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs +++ b/src/roslyn/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs @@ -183,7 +183,7 @@ private static XElement CreateCompilationOptionsElement(CompilationOptions optio } private static XElement CreateMetadataReference(string path) - => new XElement(MetadataReferenceElementName, path); + => new(MetadataReferenceElementName, path); protected static XElement CreateDocumentElement( string code, string filePath, string folders = null, ParseOptions parseOptions = null, bool isMarkup = true) diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs index bde0afd2c3e..ec31e8096e6 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/BuildHost.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -18,17 +19,23 @@ namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class BuildHost : IBuildHost { private readonly BuildHostLogger _logger; - private readonly ImmutableDictionary _globalMSBuildProperties; - private readonly string? _binaryLogPath; private readonly RpcServer _server; - private readonly object _gate = new object(); + private readonly object _gate = new(); private ProjectBuildManager? _buildManager; - public BuildHost(BuildHostLogger logger, ImmutableDictionary globalMSBuildProperties, string? binaryLogPath, RpcServer server) + /// + /// The global properties to use for all builds; should not be changed once the is initialized. + /// + private ImmutableDictionary? _globalMSBuildProperties; + + /// + /// The binary log path to use for all builds; should not be changed once the is initialized. + /// + private string? _binaryLogPath; + + public BuildHost(BuildHostLogger logger, RpcServer server) { _logger = logger; - _globalMSBuildProperties = globalMSBuildProperties; - _binaryLogPath = binaryLogPath; _server = server; } @@ -122,6 +129,9 @@ private void CreateBuildManager() if (_buildManager != null) return; + if (_globalMSBuildProperties is null) + throw new InvalidOperationException($"{nameof(ConfigureGlobalState)} should have been called first to set up global state."); + BinaryLogger? logger = null; if (_binaryLogPath != null) @@ -150,6 +160,18 @@ private void EnsureMSBuildLoaded(string projectFilePath) Contract.ThrowIfFalse(TryEnsureMSBuildLoaded(projectFilePath), $"We don't have an MSBuild to use; {nameof(HasUsableMSBuild)} should have been called first to check."); } + public void ConfigureGlobalState(ImmutableDictionary globalProperties, string? binlogPath) + { + lock (_gate) + { + if (_buildManager != null) + throw new InvalidOperationException($"{nameof(_buildManager)} has already been initialized and cannot be changed"); + + _globalMSBuildProperties = globalProperties; + _binaryLogPath = binlogPath; + } + } + /// /// Returns the target ID of the object created for this. /// diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs index a108720e30a..ffe75885208 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Program.cs @@ -15,25 +15,16 @@ internal static class Program { internal static async Task Main(string[] args) { + // Note: we should limit the data passed through via command line strings, and pass information through IBuildHost.ConfigureGlobalState whenever possible. + // This is because otherwise we might run into escaping issues, or command line length limits. + var pipeOption = new Option("--pipe") { Required = true }; - var propertyOption = new Option("--property") { Arity = ArgumentArity.ZeroOrMore }; - var binaryLogOption = new Option("--binlog") { Required = false }; var localeOption = new Option("--locale") { Required = true }; - var command = new RootCommand { pipeOption, binaryLogOption, propertyOption, localeOption }; + var command = new RootCommand { pipeOption, localeOption }; var parsedArguments = command.Parse(args); var pipeName = parsedArguments.GetValue(pipeOption)!; - var properties = parsedArguments.GetValue(propertyOption)!; - var binaryLogPath = parsedArguments.GetValue(binaryLogOption); var locale = parsedArguments.GetValue(localeOption)!; - var propertiesBuilder = ImmutableDictionary.CreateBuilder(); - - foreach (var property in properties) - { - var propertyParts = property.Split(['='], count: 2); - propertiesBuilder.Add(propertyParts[0], propertyParts[1]); - } - var logger = new BuildHostLogger(Console.Error); try @@ -53,7 +44,7 @@ internal static async Task Main(string[] args) var server = new RpcServer(pipeServer); - var targetObject = server.AddTarget(new BuildHost(logger, propertiesBuilder.ToImmutable(), binaryLogPath, server)); + var targetObject = server.AddTarget(new BuildHost(logger, 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/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs index 45be2011e2c..8310712b7a6 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/IBuildHost.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -28,6 +29,12 @@ internal interface IBuildHost /// bool HasUsableMSBuild(string projectOrSolutionFilePath); + /// + /// Called once on a new process to configure some global state. This is used for these rather than passing through command line strings, since these contain data that might + /// contain paths (which can have escaping issues) or could be quite large (which could run into length limits). + /// + void ConfigureGlobalState(ImmutableDictionary globalProperties, string? binlogPath); + Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken); /// diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs index ee75ca9bf1c..ecd950e716a 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/Contracts/JsonSettings.cs @@ -12,7 +12,7 @@ internal static class JsonSettings { public static readonly Encoding StreamEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); - public static readonly JsonSerializerSettings SingleLineSerializerSettings = new JsonSerializerSettings + public static readonly JsonSerializerSettings SingleLineSerializerSettings = new() { // Setting Formatting.None ensures each is serialized to it's own line, which we implicitly rely on Formatting = Newtonsoft.Json.Formatting.None, diff --git a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs index e161bc6fa0c..b856c0fa604 100644 --- a/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs +++ b/src/roslyn/src/Workspaces/MSBuild/BuildHost/Rpc/RpcServer.cs @@ -27,13 +27,13 @@ namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RpcServer { private readonly TextWriter _streamWriter; - private readonly SemaphoreSlim _sendingStreamSemaphore = new SemaphoreSlim(initialCount: 1); + private readonly SemaphoreSlim _sendingStreamSemaphore = new(initialCount: 1); private readonly TextReader _streamReader; private readonly ConcurrentDictionary _rpcTargets = []; private volatile int _nextRpcTargetIndex = -1; // We'll start at -1 so the first value becomes zero - private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _shutdownTokenSource = new(); public RpcServer(PipeStream stream) { diff --git a/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs b/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs index 6fb626a7f08..f0fc0b7b55c 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Core/MSBuild/BuildHostProcessManager.cs @@ -120,6 +120,8 @@ async Task NoLock_GetBuildHostAsync(BuildHostProcessKind build throw new Exception($"BuildHost process exited immediately with {process.ExitCode}"); } + await buildHostProcess.BuildHost.ConfigureGlobalStateAsync(_globalMSBuildProperties, _binaryLogPathProvider?.GetNewLogPath(), cancellationToken).ConfigureAwait(false); + if (buildHostKind != BuildHostProcessKind.NetCore || projectOrSolutionFilePath is null || dotnetPath is not null) @@ -164,7 +166,7 @@ async Task NoLock_GetBuildHostAsync(BuildHostProcessKind build #endif } - internal ProcessStartInfo CreateBuildHostStartInfo(BuildHostProcessKind buildHostKind, string pipeName, string? dotnetPath) + internal static ProcessStartInfo CreateBuildHostStartInfo(BuildHostProcessKind buildHostKind, string pipeName, string? dotnetPath) { return buildHostKind switch { @@ -219,7 +221,7 @@ public async ValueTask DisposeAsync() await process.DisposeAsync().ConfigureAwait(false); } - private ProcessStartInfo CreateDotNetCoreBuildHostStartInfo(string pipeName, string? dotnetPath) + private static ProcessStartInfo CreateDotNetCoreBuildHostStartInfo(string pipeName, string? dotnetPath) { var processStartInfo = new ProcessStartInfo() { @@ -236,7 +238,7 @@ private ProcessStartInfo CreateDotNetCoreBuildHostStartInfo(string pipeName, str AddArgument(processStartInfo, netCoreBuildHostPath); - AppendBuildHostCommandLineArgumentsConfigureProcess(processStartInfo, pipeName); + AppendBuildHostCommandLineArgumentsAndConfigureProcess(processStartInfo, pipeName); return processStartInfo; } @@ -246,7 +248,7 @@ internal static string GetNetCoreBuildHostPath() return GetBuildHostPath("BuildHost-netcore", "Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll"); } - private ProcessStartInfo CreateDotNetFrameworkBuildHostStartInfo(string pipeName) + private static ProcessStartInfo CreateDotNetFrameworkBuildHostStartInfo(string pipeName) { var netFrameworkBuildHost = GetDotNetFrameworkBuildHostPath(); var processStartInfo = new ProcessStartInfo() @@ -254,12 +256,12 @@ private ProcessStartInfo CreateDotNetFrameworkBuildHostStartInfo(string pipeName FileName = netFrameworkBuildHost, }; - AppendBuildHostCommandLineArgumentsConfigureProcess(processStartInfo, pipeName); + AppendBuildHostCommandLineArgumentsAndConfigureProcess(processStartInfo, pipeName); return processStartInfo; } - private ProcessStartInfo CreateMonoBuildHostStartInfo(string pipeName) + private static ProcessStartInfo CreateMonoBuildHostStartInfo(string pipeName) { var processStartInfo = new ProcessStartInfo { @@ -268,7 +270,7 @@ private ProcessStartInfo CreateMonoBuildHostStartInfo(string pipeName) AddArgument(processStartInfo, GetDotNetFrameworkBuildHostPath()); - AppendBuildHostCommandLineArgumentsConfigureProcess(processStartInfo, pipeName); + AppendBuildHostCommandLineArgumentsAndConfigureProcess(processStartInfo, pipeName); return processStartInfo; } @@ -305,23 +307,11 @@ private static string GetBuildHostPath(string contentFolderName, string assembly return buildHostPath; } - private void AppendBuildHostCommandLineArgumentsConfigureProcess(ProcessStartInfo processStartInfo, string pipeName) + private static void AppendBuildHostCommandLineArgumentsAndConfigureProcess(ProcessStartInfo processStartInfo, string pipeName) { AddArgument(processStartInfo, "--pipe"); AddArgument(processStartInfo, pipeName); - foreach (var globalMSBuildProperty in _globalMSBuildProperties) - { - AddArgument(processStartInfo, "--property"); - AddArgument(processStartInfo, globalMSBuildProperty.Key + '=' + globalMSBuildProperty.Value); - } - - if (_binaryLogPathProvider?.GetNewLogPath() is string binaryLogPath) - { - AddArgument(processStartInfo, "--binlog"); - AddArgument(processStartInfo, binaryLogPath); - } - AddArgument(processStartInfo, "--locale"); AddArgument(processStartInfo, System.Globalization.CultureInfo.CurrentUICulture.Name); diff --git a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs index 6edb429bfab..72df5327bc6 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RemoteBuildHost.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -37,6 +39,10 @@ public RemoteBuildHost(RpcClient client) public Task HasUsableMSBuildAsync(string projectOrSolutionFilePath, CancellationToken cancellationToken) => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.HasUsableMSBuild), parameters: [projectOrSolutionFilePath], cancellationToken); + /// + public Task ConfigureGlobalStateAsync(ImmutableDictionary globalProperties, string? binlogPath, CancellationToken cancellationToken) + => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.ConfigureGlobalState), parameters: [globalProperties, binlogPath], cancellationToken); + public async Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken) { var remoteProjectFileTargetObject = await _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.LoadProjectFileAsync), parameters: [projectFilePath, languageName], cancellationToken).ConfigureAwait(false); @@ -61,5 +67,4 @@ public async Task LoadProjectAsync(string projectFilePath, st public Task ShutdownAsync(CancellationToken cancellationToken) => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.ShutdownAsync), parameters: [], cancellationToken); - } diff --git a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs index cc93ceb6ba2..ee14f340a91 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Core/Rpc/RpcClient.cs @@ -30,13 +30,13 @@ internal sealed class RpcClient /// /// A semaphore taken to synchronize all writes to . /// - private readonly SemaphoreSlim _streamWritingSemaphore = new SemaphoreSlim(initialCount: 1); + private readonly SemaphoreSlim _streamWritingSemaphore = new(initialCount: 1); private readonly TextReader _receivingStreamReader; private readonly ConcurrentDictionary, System.Type? expectedReturnType)> _outstandingRequests = []; private volatile int _nextRequestId = 0; - private readonly CancellationTokenSource _shutdownTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _shutdownTokenSource = new(); public RpcClient(PipeStream stream) { diff --git a/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs b/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs index 4a3bc61b6e0..a3d0a1de06c 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Test/BuildHostProcessManagerTests.cs @@ -17,8 +17,7 @@ public sealed class BuildHostProcessManagerTests [Fact] public void ProcessStartInfo_ForNetCore_RollsForwardToLatestPreview() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); #if NET var rollForwardIndex = processStartInfo.ArgumentList.IndexOf("--roll-forward"); @@ -34,8 +33,7 @@ public void ProcessStartInfo_ForNetCore_RollsForwardToLatestPreview() [Fact] public void ProcessStartInfo_ForNetCore_LaunchesDotNetCLI() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.NetCore, pipeName: "", dotnetPath: null); Assert.StartsWith("dotnet", processStartInfo.FileName); } @@ -43,8 +41,7 @@ public void ProcessStartInfo_ForNetCore_LaunchesDotNetCLI() [Fact] public void ProcessStartInfo_ForMono_LaunchesMono() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.Mono, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.Mono, pipeName: "", dotnetPath: null); Assert.Equal("mono", processStartInfo.FileName); } @@ -52,35 +49,11 @@ public void ProcessStartInfo_ForMono_LaunchesMono() [Fact] public void ProcessStartInfo_ForNetFramework_LaunchesBuildHost() { - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(BuildHostProcessKind.NetFramework, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(BuildHostProcessKind.NetFramework, pipeName: "", dotnetPath: null); Assert.EndsWith("Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.exe", processStartInfo.FileName); } - [Theory] - [InlineData(BuildHostProcessKind.NetFramework)] - [InlineData(BuildHostProcessKind.NetCore)] - [InlineData(BuildHostProcessKind.Mono)] - internal void ProcessStartInfo_PassesBinLogPath(BuildHostProcessKind buildHostKind) - { - const string BinaryLogPath = "test.binlog"; - - var binLogPathProviderMock = new Mock(MockBehavior.Strict); - binLogPathProviderMock.Setup(m => m.GetNewLogPath()).Returns(BinaryLogPath); - - var processStartInfo = new BuildHostProcessManager(binaryLogPathProvider: binLogPathProviderMock.Object) - .CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); - -#if NET - var binlogIndex = processStartInfo.ArgumentList.IndexOf("--binlog"); - Assert.True(binlogIndex >= 0); - Assert.Equal(BinaryLogPath, processStartInfo.ArgumentList[binlogIndex + 1]); -#else - Assert.Contains($"--binlog {BinaryLogPath}", processStartInfo.Arguments); -#endif - } - [Theory] [InlineData(BuildHostProcessKind.NetFramework)] [InlineData(BuildHostProcessKind.NetCore)] @@ -89,8 +62,7 @@ internal void ProcessStartInfo_PassesPipeName(BuildHostProcessKind buildHostKind { const string PipeName = "TestPipe"; - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(buildHostKind, PipeName, dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(buildHostKind, PipeName, dotnetPath: null); #if NET var binlogIndex = processStartInfo.ArgumentList.IndexOf("--pipe"); @@ -110,8 +82,7 @@ internal void ProcessStartInfo_PassesLocale(BuildHostProcessKind buildHostKind) { const string Locale = "de-DE"; - var processStartInfo = new BuildHostProcessManager() - .CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); + var processStartInfo = BuildHostProcessManager.CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); #if NET var localeIndex = processStartInfo.ArgumentList.IndexOf("--locale"); @@ -121,42 +92,4 @@ internal void ProcessStartInfo_PassesLocale(BuildHostProcessKind buildHostKind) Assert.Contains($"--locale {Locale}", processStartInfo.Arguments); #endif } - - [Theory] - [InlineData(BuildHostProcessKind.NetFramework)] - [InlineData(BuildHostProcessKind.NetCore)] - [InlineData(BuildHostProcessKind.Mono)] - internal void ProcessStartInfo_PassesProperties(BuildHostProcessKind buildHostKind) - { - var globalMSBuildProperties = new Dictionary - { - ["TestKey1"] = "TestValue1", - ["TestKey2"] = "TestValue2", - }.ToImmutableDictionary(); - - var buildHostProcessManager = new BuildHostProcessManager(globalMSBuildProperties); - - var processStartInfo = buildHostProcessManager.CreateBuildHostStartInfo(buildHostKind, pipeName: "", dotnetPath: null); - -#if NET - foreach (var kvp in globalMSBuildProperties) - { - var propertyArgument = GetPropertyArgument(kvp); - var argumentIndex = processStartInfo.ArgumentList.IndexOf(propertyArgument); - Assert.True(argumentIndex >= 0); - Assert.Equal("--property", processStartInfo.ArgumentList[argumentIndex - 1]); - } -#else - foreach (var kvp in globalMSBuildProperties) - { - var propertyArgument = GetPropertyArgument(kvp); - Assert.Contains($"--property {propertyArgument}", processStartInfo.Arguments); - } -#endif - - static string GetPropertyArgument(KeyValuePair property) - { - return $"{property.Key}={property.Value}"; - } - } } diff --git a/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs b/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs index 3b239de1cc6..da5b88390c2 100644 --- a/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs +++ b/src/roslyn/src/Workspaces/MSBuild/Test/NewlyCreatedProjectsFromDotNetNew.cs @@ -62,7 +62,14 @@ public NewlyCreatedProjectsFromDotNetNew(ITestOutputHelper testOutput) : base(te [ConditionalTheory(typeof(DotNetSdkMSBuildInstalled))] [MemberData(nameof(GetCSharpProjectTemplateNames), DisableDiscoveryEnumeration = false)] public Task ValidateCSharpTemplateProjects(string templateName) - => AssertTemplateProjectLoadsCleanlyAsync(templateName, LanguageNames.CSharp); + { + if (templateName is "blazor" or "blazorwasm") + { + // https://github.com/dotnet/roslyn/issues/80263 + return Task.CompletedTask; + } + return AssertTemplateProjectLoadsCleanlyAsync(templateName, LanguageNames.CSharp); + } [ConditionalTheory(typeof(DotNetSdkMSBuildInstalled))] [MemberData(nameof(GetVisualBasicProjectTemplateNames), DisableDiscoveryEnumeration = false)] @@ -218,6 +225,7 @@ async Task AssertProjectLoadsCleanlyAsync(string projectFilePath, string[] ignor AssertEx.Empty(workspace.Diagnostics, $"The following workspace diagnostics are being reported for the template."); var compilation = await project.GetRequiredCompilationAsync(CancellationToken.None); + AssertEx.Empty(await project.GetSourceGeneratorDiagnosticsAsync(CancellationToken.None), $"The following source generator diagnostics are being reported for the template."); // Unnecessary using directives are reported with a severity of Hidden var nonHiddenDiagnostics = compilation!.GetDiagnostics() diff --git a/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs b/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs index 01ea9b3af13..4096efe5c4c 100644 --- a/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs +++ b/src/roslyn/src/Workspaces/Remote/Core/ExportProviderBuilder.cs @@ -11,11 +11,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Composition; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; @@ -80,6 +78,10 @@ private async Task GetCompositionConfigurationAsync(Canc new AttributedPartDiscoveryV1(Resolver)); var parts = await discovery.CreatePartsAsync(AssemblyPaths, progress: null, cancellationToken).ConfigureAwait(false); + + // Work around https://github.com/microsoft/vs-mef/issues/620 -- parts might be incomplete if the cancellationToken was cancelled + cancellationToken.ThrowIfCancellationRequested(); + var catalog = ComposableCatalog.Create(Resolver) .AddParts(parts) .WithCompositionService(); // Makes an ICompositionService export available to MEF parts to import diff --git a/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs b/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs index aec8f687cb4..ab21fb5e4d7 100644 --- a/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs +++ b/src/roslyn/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs @@ -54,7 +54,7 @@ internal sealed class ProjectIdFormatter : IMessagePackFormatter /// to only serialize or deserialize it's DebugName once. Additionally, this cache allows /// the Deserialization code to only construct the ProjectID a single time. /// - private readonly ConcurrentDictionary _projectIds = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _projectIds = new(); public ProjectId? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) { diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs index 6a0d9a2c672..48bcac81ede 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspaceManager.cs @@ -50,7 +50,7 @@ internal class RemoteWorkspaceManager return new RemoteWorkspaceManager(CreateAssetCache); static SolutionAssetCache CreateAssetCache(RemoteWorkspace workspace) - => new SolutionAssetCache(workspace, cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1)); + => new(workspace, cleanupInterval: TimeSpan.FromSeconds(30), purgeAfter: TimeSpan.FromMinutes(1)); }); internal static RemoteWorkspaceManager Default => s_default.Value; diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotProposalAdjusterService.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotProposalAdjusterService.cs index 0a39e3bbee8..f3aa0a36dcc 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotProposalAdjusterService.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/Copilot/RemoteCopilotProposalAdjusterService.cs @@ -21,14 +21,14 @@ protected override IRemoteCopilotProposalAdjusterService CreateService(in Servic => new RemoteCopilotProposalAdjusterService(arguments); } - public ValueTask> TryAdjustProposalAsync(Checksum solutionChecksum, DocumentId documentId, ImmutableArray textChanges, CancellationToken cancellationToken) + public ValueTask TryAdjustProposalAsync(Checksum solutionChecksum, DocumentId documentId, ImmutableArray textChanges, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => { var document = await solution.GetRequiredDocumentAsync( documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); - var service = solution.Services.GetRequiredService(); + var service = document.GetRequiredLanguageService(); return await service.TryAdjustProposalAsync(document, textChanges, cancellationToken).ConfigureAwait(false); }, cancellationToken); } diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs deleted file mode 100644 index a8fbc902f21..00000000000 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ /dev/null @@ -1,493 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Telemetry; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Remote.Diagnostics; - -internal sealed class DiagnosticComputer -{ - /// - /// Cache of and a map from analyzer IDs to s - /// for all analyzers for the last project to be analyzed. The instance is - /// shared between all the following document analyses modes for the project: - /// - /// Span-based analysis for active document (lightbulb) - /// Background analysis for active and open documents. - /// - /// NOTE: We do not re-use this cache for project analysis as it leads to significant memory increase in the OOP - /// process. Additionally, we only store the cache entry for the last project to be analyzed instead of maintaining - /// a CWT keyed off each project in the solution, as the CWT does not seem to drop entries until ForceGC happens, - /// leading to significant memory pressure when there are large number of open documents across different projects - /// to be analyzed by background analysis. - /// - private static CompilationWithAnalyzersCacheEntry? s_compilationWithAnalyzersCache = null; - - /// - /// Static gate controlling access to following static fields: - /// - - /// - private static readonly object s_gate = new(); - - /// - /// Solution checksum for the diagnostic request. - /// We use this checksum and the of the diagnostic request as the key - /// to the . - /// - private readonly Checksum _solutionChecksum; - - private readonly TextDocument? _document; - private readonly Project _project; - private readonly TextSpan? _span; - private readonly AnalysisKind? _analysisKind; - private readonly IPerformanceTrackerService? _performanceTracker; - private readonly DiagnosticAnalyzerInfoCache _analyzerInfoCache; - private readonly HostWorkspaceServices _hostWorkspaceServices; - - private DiagnosticComputer( - TextDocument? document, - Project project, - Checksum solutionChecksum, - TextSpan? span, - AnalysisKind? analysisKind, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - HostWorkspaceServices hostWorkspaceServices) - { - _document = document; - _project = project; - _solutionChecksum = solutionChecksum; - _span = span; - _analysisKind = analysisKind; - _analyzerInfoCache = analyzerInfoCache; - _hostWorkspaceServices = hostWorkspaceServices; - _performanceTracker = project.Solution.Services.GetService(); - } - - public static Task GetDiagnosticsAsync( - TextDocument? document, - Project project, - Checksum solutionChecksum, - TextSpan? span, - ImmutableArray projectAnalyzerIds, - ImmutableArray hostAnalyzerIds, - AnalysisKind? analysisKind, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - HostWorkspaceServices hostWorkspaceServices, - bool logPerformanceInfo, - bool getTelemetryInfo, - CancellationToken cancellationToken) - { - // PERF: Due to the concept of InFlight solution snapshots in OOP process, we might have been - // handed a Project instance that does not match the Project instance corresponding to our - // cached CompilationWithAnalyzers instance, while the underlying Solution checksum matches - // for our cached entry and the incoming request. - // We detect this case upfront here and re-use the cached CompilationWithAnalyzers and Project - // instance for diagnostic computation, thus improving the performance of analyzer execution. - // This is an important performance optimization for lightbulb diagnostic computation. - // See https://github.com/dotnet/roslyn/issues/66968 for details. - lock (s_gate) - { - if (s_compilationWithAnalyzersCache?.SolutionChecksum == solutionChecksum && - s_compilationWithAnalyzersCache.Project.Id == project.Id && - s_compilationWithAnalyzersCache.Project != project) - { - project = s_compilationWithAnalyzersCache.Project; - if (document != null) - document = project.GetTextDocument(document.Id); - } - } - - // We execute explicit, user-invoked diagnostics requests with higher priority compared to implicit requests - // from clients such as editor diagnostic tagger to show squiggles, background analysis to populate the error list, etc. - var diagnosticsComputer = new DiagnosticComputer(document, project, solutionChecksum, span, analysisKind, analyzerInfoCache, hostWorkspaceServices); - return diagnosticsComputer.GetDiagnosticsAsync( - projectAnalyzerIds, hostAnalyzerIds, logPerformanceInfo, getTelemetryInfo, cancellationToken); - } - - private async Task GetDiagnosticsAsync( - ImmutableArray projectAnalyzerIds, - ImmutableArray hostAnalyzerIds, - bool logPerformanceInfo, - bool getTelemetryInfo, - CancellationToken cancellationToken) - { - var (compilationWithAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap) = await GetOrCreateCompilationWithAnalyzersAsync(cancellationToken).ConfigureAwait(false); - if (compilationWithAnalyzers == null) - return DiagnosticAnalysisResults.Empty; - - var (projectAnalyzers, hostAnalyzers) = GetAnalyzers(projectAnalyzerToIdMap, hostAnalyzerToIdMap, projectAnalyzerIds, hostAnalyzerIds); - if (projectAnalyzers.IsEmpty && hostAnalyzers.IsEmpty) - return DiagnosticAnalysisResults.Empty; - - if (_document == null) - { - if (projectAnalyzers.Length < compilationWithAnalyzers.ProjectAnalyzers.Length) - { - Contract.ThrowIfFalse(projectAnalyzers.Length > 0 || compilationWithAnalyzers.HostCompilationWithAnalyzers is not null); - - // PERF: Generate a new CompilationWithAnalyzers with trimmed analyzers for non-document analysis case. - compilationWithAnalyzers = new CompilationWithAnalyzersPair( - projectAnalyzers.Any() ? compilationWithAnalyzers.ProjectCompilation!.WithAnalyzers(projectAnalyzers, compilationWithAnalyzers.ProjectCompilationWithAnalyzers!.AnalysisOptions) : null, - compilationWithAnalyzers.HostCompilationWithAnalyzers); - } - - if (hostAnalyzers.Length < compilationWithAnalyzers.HostAnalyzers.Length) - { - Contract.ThrowIfFalse(hostAnalyzers.Length > 0 || compilationWithAnalyzers.ProjectCompilationWithAnalyzers is not null); - - // PERF: Generate a new CompilationWithAnalyzers with trimmed analyzers for non-document analysis case. - compilationWithAnalyzers = new CompilationWithAnalyzersPair( - compilationWithAnalyzers.ProjectCompilationWithAnalyzers, - hostAnalyzers.Any() ? compilationWithAnalyzers.HostCompilation!.WithAnalyzers(hostAnalyzers, compilationWithAnalyzers.HostCompilationWithAnalyzers!.AnalysisOptions) : null); - } - } - - var skippedAnalyzersInfo = _project.Solution.SolutionState.Analyzers.GetSkippedAnalyzersInfo( - _project.State, _analyzerInfoCache); - - return await AnalyzeAsync(compilationWithAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap, projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, - logPerformanceInfo, getTelemetryInfo, cancellationToken).ConfigureAwait(false); - } - - private async Task AnalyzeAsync( - CompilationWithAnalyzersPair compilationWithAnalyzers, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, - SkippedHostAnalyzersInfo skippedAnalyzersInfo, - bool logPerformanceInfo, - bool getTelemetryInfo, - CancellationToken cancellationToken) - { - var documentAnalysisScope = _document != null - ? new DocumentAnalysisScope(_document, _span, projectAnalyzers, hostAnalyzers, _analysisKind!.Value) - : null; - - var (analysisResult, additionalPragmaSuppressionDiagnostics) = await compilationWithAnalyzers.GetAnalysisResultAsync( - documentAnalysisScope, _project, _analyzerInfoCache, cancellationToken).ConfigureAwait(false); - - if (logPerformanceInfo && _performanceTracker != null) - { - // Only log telemetry snapshot is we have an active telemetry session, - // i.e. user has not opted out of reporting telemetry. - var telemetryService = _hostWorkspaceServices.GetRequiredService(); - if (telemetryService.HasActiveSession) - { - // +1 to include project itself - var unitCount = 1; - if (documentAnalysisScope == null) - unitCount += _project.DocumentIds.Count; - - var performanceInfo = analysisResult?.MergedAnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(_analyzerInfoCache) ?? []; - _performanceTracker.AddSnapshot(performanceInfo, unitCount, forSpanAnalysis: _span.HasValue); - } - } - - var builderMap = ImmutableDictionary.Empty; - if (analysisResult is not null) - { - builderMap = builderMap.AddRange(await analysisResult.ToResultBuilderMapAsync( - additionalPragmaSuppressionDiagnostics, documentAnalysisScope, - _project, projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo, cancellationToken).ConfigureAwait(false)); - } - - var telemetry = getTelemetryInfo - ? GetTelemetryInfo(analysisResult, projectAnalyzers, hostAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap) - : []; - - return new DiagnosticAnalysisResults(Dehydrate(builderMap, projectAnalyzerToIdMap, hostAnalyzerToIdMap), telemetry); - } - - private static ImmutableArray<(string analyzerId, DiagnosticMap diagnosticMap)> Dehydrate( - ImmutableDictionary builderMap, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap) - { - var diagnostics = new FixedSizeArrayBuilder<(string analyzerId, DiagnosticMap diagnosticMap)>(builderMap.Count); - - foreach (var (analyzer, analyzerResults) in builderMap) - { - var analyzerId = GetAnalyzerId(projectAnalyzerToIdMap, hostAnalyzerToIdMap, analyzer); - - diagnostics.Add((analyzerId, - new DiagnosticMap( - analyzerResults.SyntaxLocals.SelectAsArray(entry => (entry.Key, entry.Value)), - analyzerResults.SemanticLocals.SelectAsArray(entry => (entry.Key, entry.Value)), - analyzerResults.NonLocals.SelectAsArray(entry => (entry.Key, entry.Value)), - analyzerResults.Others))); - } - - return diagnostics.MoveToImmutable(); - } - - private static ImmutableArray<(string analyzerId, AnalyzerTelemetryInfo)> GetTelemetryInfo( - AnalysisResultPair? analysisResult, - ImmutableArray projectAnalyzers, - ImmutableArray hostAnalyzers, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap) - { - Func shouldInclude; - if (projectAnalyzers.Length < (analysisResult?.ProjectAnalysisResult?.AnalyzerTelemetryInfo.Count ?? 0) - || hostAnalyzers.Length < (analysisResult?.HostAnalysisResult?.AnalyzerTelemetryInfo.Count ?? 0)) - { - // Filter the telemetry info to the executed analyzers. - using var _1 = PooledHashSet.GetInstance(out var analyzersSet); - analyzersSet.AddRange(projectAnalyzers); - analyzersSet.AddRange(hostAnalyzers); - - shouldInclude = analyzer => analyzersSet.Contains(analyzer); - } - else - { - shouldInclude = _ => true; - } - - using var _2 = ArrayBuilder<(string analyzerId, AnalyzerTelemetryInfo)>.GetInstance(out var telemetryBuilder); - if (analysisResult is not null) - { - foreach (var (analyzer, analyzerTelemetry) in analysisResult.MergedAnalyzerTelemetryInfo) - { - if (shouldInclude(analyzer)) - { - var analyzerId = GetAnalyzerId(projectAnalyzerToIdMap, hostAnalyzerToIdMap, analyzer); - telemetryBuilder.Add((analyzerId, analyzerTelemetry)); - } - } - } - - return telemetryBuilder.ToImmutableAndClear(); - } - - private static string GetAnalyzerId(BidirectionalMap analyzerMap1, BidirectionalMap analyzerMap2, DiagnosticAnalyzer analyzer) - { - var analyzerId = analyzerMap1.GetKeyOrDefault(analyzer) ?? analyzerMap2.GetKeyOrDefault(analyzer); - Contract.ThrowIfNull(analyzerId); - - return analyzerId; - } - - private static (ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers) GetAnalyzers(BidirectionalMap projectAnalyzerMap, BidirectionalMap hostAnalyzerMap, ImmutableArray projectAnalyzerIds, ImmutableArray hostAnalyzerIds) - { - // TODO: this probably need to be cached as well in analyzer service? - var projectBuilder = ImmutableArray.CreateBuilder(); - var hostBuilder = ImmutableArray.CreateBuilder(); - - foreach (var analyzerId in projectAnalyzerIds) - { - if (projectAnalyzerMap.TryGetValue(analyzerId, out var analyzer)) - { - projectBuilder.Add(analyzer); - } - } - - foreach (var analyzerId in hostAnalyzerIds) - { - if (hostAnalyzerMap.TryGetValue(analyzerId, out var analyzer)) - { - hostBuilder.Add(analyzer); - } - } - - var projectAnalyzers = projectBuilder.ToImmutableAndClear(); - - if (hostAnalyzerIds.Any()) - { - // If any host analyzers are active, make sure to also include any project diagnostic suppressors - var projectSuppressors = projectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor); - // Make sure to remove any project suppressors already in the host analyzer array so we don't end up with - // duplicates. - hostBuilder.RemoveRange(projectSuppressors); - hostBuilder.AddRange(projectSuppressors); - } - - return (projectAnalyzers, hostBuilder.ToImmutableAndClear()); - } - - private async Task<(CompilationWithAnalyzersPair? compilationWithAnalyzers, BidirectionalMap projectAnalyzerToIdMap, BidirectionalMap hostAnalyzerToIdMap)> GetOrCreateCompilationWithAnalyzersAsync(CancellationToken cancellationToken) - { - var cacheEntry = await GetOrCreateCacheEntryAsync().ConfigureAwait(false); - return (cacheEntry.CompilationWithAnalyzers, cacheEntry.ProjectAnalyzerToIdMap, cacheEntry.HostAnalyzerToIdMap); - - async Task GetOrCreateCacheEntryAsync() - { - if (_document == null) - { - // Only use cache for document analysis. - return await CreateCompilationWithAnalyzersCacheEntryAsync(cancellationToken).ConfigureAwait(false); - } - - lock (s_gate) - { - if (s_compilationWithAnalyzersCache?.SolutionChecksum == _solutionChecksum && - s_compilationWithAnalyzersCache.Project == _project) - { - return s_compilationWithAnalyzersCache; - } - } - - var entry = await CreateCompilationWithAnalyzersCacheEntryAsync(cancellationToken).ConfigureAwait(false); - - lock (s_gate) - { - s_compilationWithAnalyzersCache = entry; - } - - return entry; - } - } - - private async Task CreateCompilationWithAnalyzersCacheEntryAsync(CancellationToken cancellationToken) - { - // We could consider creating a service so that we don't do this repeatedly if this shows up as perf cost - using var pooledObject = SharedPools.Default>().GetPooledObject(); - using var pooledMapProjectAnalyzerMap = SharedPools.Default>().GetPooledObject(); - using var pooledMapHostAnalyzerMap = SharedPools.Default>().GetPooledObject(); - var referenceSet = pooledObject.Object; - var projectAnalyzerMapBuilder = pooledMapProjectAnalyzerMap.Object; - var hostAnalyzerMapBuilder = pooledMapHostAnalyzerMap.Object; - - // This follows what we do in DiagnosticAnalyzerInfoCache.CheckAnalyzerReferenceIdentity - using var _1 = ArrayBuilder.GetInstance(out var projectAnalyzerBuilder); - using var _2 = ArrayBuilder.GetInstance(out var hostAnalyzerBuilder); - foreach (var reference in _project.Solution.AnalyzerReferences) - { - if (!referenceSet.Add(reference.Id)) - { - continue; - } - - var analyzers = reference.GetAnalyzers(_project.Language); - - // At times some Host analyzers should be treated as project analyzers and - // not be given access to the Host fallback options. In particular when we - // replace SDK CodeStyle analyzers with the Features analyzers. - if (ShouldRedirectAnalyzers(_project, reference)) - { - projectAnalyzerBuilder.AddRange(analyzers); - projectAnalyzerMapBuilder.AppendAnalyzerMap(analyzers); - } - else - { - hostAnalyzerBuilder.AddRange(analyzers); - hostAnalyzerMapBuilder.AppendAnalyzerMap(analyzers); - } - } - - // Clear the set -- we want these two loops to be independent - referenceSet.Clear(); - - foreach (var reference in _project.AnalyzerReferences) - { - if (!referenceSet.Add(reference.Id)) - { - continue; - } - - var analyzers = reference.GetAnalyzers(_project.Language); - projectAnalyzerBuilder.AddRange(analyzers); - - var projectSuppressors = analyzers.WhereAsArray(static a => a is DiagnosticSuppressor); - // Make sure to remove any project suppressors already in the host analyzer array so we don't end up with - // duplicates. - hostAnalyzerBuilder.RemoveRange(projectSuppressors); - hostAnalyzerBuilder.AddRange(projectSuppressors); - - projectAnalyzerMapBuilder.AppendAnalyzerMap(analyzers); - } - - var compilationWithAnalyzers = projectAnalyzerBuilder.Count > 0 || hostAnalyzerBuilder.Count > 0 - ? await CreateCompilationWithAnalyzerAsync(projectAnalyzerBuilder.ToImmutable(), hostAnalyzerBuilder.ToImmutable(), cancellationToken).ConfigureAwait(false) - : null; - var projectAnalyzerToIdMap = new BidirectionalMap(projectAnalyzerMapBuilder); - var hostAnalyzerToIdMap = new BidirectionalMap(hostAnalyzerMapBuilder); - - return new CompilationWithAnalyzersCacheEntry(_solutionChecksum, _project, compilationWithAnalyzers, projectAnalyzerToIdMap, hostAnalyzerToIdMap); - - static bool ShouldRedirectAnalyzers(Project project, AnalyzerReference reference) - { - // When replacing SDK CodeStyle analyzers we should redirect Features analyzers - // so they are treated as project analyzers. - return project.State.HasSdkCodeStyleAnalyzers && reference.IsFeaturesAnalyzer(); - } - } - - private async Task CreateCompilationWithAnalyzerAsync(ImmutableArray projectAnalyzers, ImmutableArray hostAnalyzers, CancellationToken cancellationToken) - { - Contract.ThrowIfFalse(!projectAnalyzers.IsEmpty || !hostAnalyzers.IsEmpty); - - // Always run analyzers concurrently in OOP - const bool concurrentAnalysis = true; - - // Get original compilation - var compilation = await _project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - - // Fork compilation with concurrent build. this is okay since WithAnalyzers will fork compilation - // anyway to attach event queue. This should make compiling compilation concurrent and make things - // faster - compilation = compilation.WithOptions(compilation.Options.WithConcurrentBuild(concurrentAnalysis)); - - // Run analyzers concurrently, with performance logging and reporting suppressed diagnostics. - // This allows all client requests with or without performance data and/or suppressed diagnostics to be satisfied. - // TODO: can we support analyzerExceptionFilter in remote host? - // right now, host doesn't support watson, we might try to use new NonFatal watson API? - var projectAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: _project.AnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: null, - concurrentAnalysis: concurrentAnalysis, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - var hostAnalyzerOptions = new CompilationWithAnalyzersOptions( - options: _project.HostAnalyzerOptions, - onAnalyzerException: null, - analyzerExceptionFilter: null, - concurrentAnalysis: concurrentAnalysis, - logAnalyzerExecutionTime: true, - reportSuppressedDiagnostics: true); - - return new CompilationWithAnalyzersPair( - projectAnalyzers.Any() ? compilation.WithAnalyzers(projectAnalyzers, projectAnalyzerOptions) : null, - hostAnalyzers.Any() ? compilation.WithAnalyzers(hostAnalyzers, hostAnalyzerOptions) : null); - } - - private sealed class CompilationWithAnalyzersCacheEntry - { - public Checksum SolutionChecksum { get; } - public Project Project { get; } - public CompilationWithAnalyzersPair? CompilationWithAnalyzers { get; } - public BidirectionalMap ProjectAnalyzerToIdMap { get; } - public BidirectionalMap HostAnalyzerToIdMap { get; } - - public CompilationWithAnalyzersCacheEntry( - Checksum solutionChecksum, - Project project, - CompilationWithAnalyzersPair? compilationWithAnalyzers, - BidirectionalMap projectAnalyzerToIdMap, - BidirectionalMap hostAnalyzerToIdMap) - { - SolutionChecksum = solutionChecksum; - Project = project; - CompilationWithAnalyzers = compilationWithAnalyzers; - ProjectAnalyzerToIdMap = projectAnalyzerToIdMap; - HostAnalyzerToIdMap = hostAnalyzerToIdMap; - } - } -} diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs index 1aee6ea5aef..8a11b0ee918 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs @@ -186,7 +186,7 @@ private static void Reset( /// private sealed class AnalyzerNumberAssigner { - public static readonly AnalyzerNumberAssigner Instance = new AnalyzerNumberAssigner(); + public static readonly AnalyzerNumberAssigner Instance = new(); private int _currentId; diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs index 5a22f206502..79639530182 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceTrackerService.cs @@ -37,7 +37,7 @@ internal sealed class PerformanceTrackerService : IPerformanceTrackerService private static readonly Func, int, bool, string> s_snapshotLogger = SnapshotLogger; private readonly PerformanceQueue _queueForDocumentAnalysis, _queueForSpanAnalysis; - private readonly ConcurrentDictionary _builtInMap = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 10); + private readonly ConcurrentDictionary _builtInMap = new(concurrencyLevel: 2, capacity: 10); public event EventHandler SnapshotAdded; diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs index a4dc0cc9a35..f7a9c14a224 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; @@ -24,14 +25,43 @@ protected override IRemoteDiagnosticAnalyzerService CreateService(in ServiceCons => new RemoteDiagnosticAnalyzerService(arguments); } - public ValueTask> ProduceProjectDiagnosticsAsync( + public ValueTask> ForceRunCodeAnalysisDiagnosticsAsync( + Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var project = solution.GetRequiredProject(projectId); + var service = solution.Services.GetRequiredService(); + return await service.ForceRunCodeAnalysisDiagnosticsAsync( + project, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } + + public ValueTask IsAnyDiagnosticIdDeprioritizedAsync( + Checksum solutionChecksum, ProjectId projectId, ImmutableArray diagnosticIds, CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var project = solution.GetRequiredProject(projectId); + var service = solution.Services.GetRequiredService(); + + return await service.IsAnyDiagnosticIdDeprioritizedAsync( + project, diagnosticIds, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } + + public ValueTask> GetDiagnosticsForIdsAsync( Checksum solutionChecksum, ProjectId projectId, - ImmutableHashSet analyzerIds, - ImmutableHashSet? diagnosticIds, ImmutableArray documentIds, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics, - bool includeProjectNonLocalResult, CancellationToken cancellationToken) { return RunWithSolutionAsync( @@ -39,14 +69,30 @@ public ValueTask> ProduceProjectDiagnosticsAsync( async solution => { var project = solution.GetRequiredProject(projectId); - var service = (DiagnosticAnalyzerService)solution.Services.GetRequiredService(); + var service = solution.Services.GetRequiredService(); - var allProjectAnalyzers = service.GetProjectAnalyzers(project); + return await service.GetDiagnosticsForIdsAsync( + project, documentIds, diagnosticIds, analyzerFilter, + includeLocalDocumentDiagnostics, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } - return await service.ProduceProjectDiagnosticsAsync( - project, allProjectAnalyzers.FilterAnalyzers(analyzerIds), diagnosticIds, documentIds, - includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, includeProjectNonLocalResult, - cancellationToken).ConfigureAwait(false); + public ValueTask> GetProjectDiagnosticsForIdsAsync( + Checksum solutionChecksum, ProjectId projectId, + ImmutableHashSet? diagnosticIds, + AnalyzerFilter analyzerFilter, + CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var project = solution.GetRequiredProject(projectId); + var service = solution.Services.GetRequiredService(); + + return await service.GetProjectDiagnosticsForIdsAsync( + project, diagnosticIds, analyzerFilter, cancellationToken).ConfigureAwait(false); }, cancellationToken); } @@ -112,6 +158,20 @@ public ValueTask> GetDiagnosticDescript cancellationToken); } + public ValueTask> GetCompilationEndDiagnosticDescriptorIdsAsync( + Checksum solutionChecksum, CancellationToken cancellationToken) + { + return RunWithSolutionAsync( + solutionChecksum, + async solution => + { + var service = solution.Services.GetRequiredService(); + return await service.GetCompilationEndDiagnosticDescriptorIdsAsync( + solution, cancellationToken).ConfigureAwait(false); + }, + cancellationToken); + } + public ValueTask> GetDiagnosticDescriptorsAsync( Checksum solutionChecksum, ProjectId projectId, @@ -137,6 +197,7 @@ public ValueTask> GetDiagnosticDescript public ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( Checksum solutionChecksum, + ProjectId? projectId, CancellationToken cancellationToken) { return RunWithSolutionAsync( @@ -144,7 +205,7 @@ public ValueTask { var service = solution.Services.GetRequiredService(); - var map = await service.GetDiagnosticDescriptorsPerReferenceAsync(solution, cancellationToken).ConfigureAwait(false); + var map = await service.GetDiagnosticDescriptorsPerReferenceAsync(solution, projectId, cancellationToken).ConfigureAwait(false); return map.ToImmutableDictionary( kvp => kvp.Key, kvp => kvp.Value.SelectAsArray(DiagnosticDescriptorData.Create)); @@ -153,54 +214,13 @@ public ValueTask>> GetDiagnosticDescriptorsPerReferenceAsync( + public ValueTask> GetDiagnosticsForSpanAsync( Checksum solutionChecksum, - ProjectId projectId, - CancellationToken cancellationToken) - { - return RunWithSolutionAsync( - solutionChecksum, - async solution => - { - var project = solution.GetRequiredProject(projectId); - var service = solution.Services.GetRequiredService(); - var map = await service.GetDiagnosticDescriptorsPerReferenceAsync(project, cancellationToken).ConfigureAwait(false); - return map.ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value.SelectAsArray(DiagnosticDescriptorData.Create)); - - }, - cancellationToken); - } - - public ValueTask> GetDeprioritizationCandidatesAsync( - Checksum solutionChecksum, ProjectId projectId, ImmutableHashSet analyzerIds, CancellationToken cancellationToken) - { - return RunWithSolutionAsync( - solutionChecksum, - async solution => - { - var project = solution.GetRequiredProject(projectId); - var service = (DiagnosticAnalyzerService)solution.Services.GetRequiredService(); - - var allProjectAnalyzers = service.GetProjectAnalyzers(project); - - var candidates = await service.GetDeprioritizationCandidatesAsync( - project, allProjectAnalyzers.FilterAnalyzers(analyzerIds), cancellationToken).ConfigureAwait(false); - - return candidates.Select(c => c.GetAnalyzerId()).ToImmutableHashSet(); - }, - cancellationToken); - } - - public ValueTask> ComputeDiagnosticsAsync( - Checksum solutionChecksum, DocumentId documentId, TextSpan? range, - ImmutableHashSet allAnalyzerIds, - ImmutableHashSet syntaxAnalyzersIds, - ImmutableHashSet semanticSpanAnalyzersIds, - ImmutableHashSet semanticDocumentAnalyzersIds, - bool incrementalAnalysis, - bool logPerformanceInfo, + DocumentId documentId, + TextSpan? range, + DiagnosticIdFilter diagnosticIdFilter, + CodeActionRequestPriority? priority, + DiagnosticKind diagnosticKind, CancellationToken cancellationToken) { return RunWithSolutionAsync( @@ -209,17 +229,10 @@ public ValueTask> ComputeDiagnosticsAsync( { var document = await solution.GetRequiredTextDocumentAsync( documentId, cancellationToken).ConfigureAwait(false); - var service = (DiagnosticAnalyzerService)solution.Services.GetRequiredService(); - - var allProjectAnalyzers = service.GetProjectAnalyzers(document.Project); + var service = solution.Services.GetRequiredService(); - return await service.ComputeDiagnosticsAsync( - document, range, - allProjectAnalyzers.FilterAnalyzers(allAnalyzerIds), - allProjectAnalyzers.FilterAnalyzers(syntaxAnalyzersIds), - allProjectAnalyzers.FilterAnalyzers(semanticSpanAnalyzersIds), - allProjectAnalyzers.FilterAnalyzers(semanticDocumentAnalyzersIds), - incrementalAnalysis, logPerformanceInfo, cancellationToken).ConfigureAwait(false); + return await service.GetDiagnosticsForSpanAsync( + document, range, diagnosticIdFilter, priority, diagnosticKind, cancellationToken).ConfigureAwait(false); }, cancellationToken); } diff --git a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs index 700ec0765e2..ddb7f483655 100644 --- a/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs +++ b/src/roslyn/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs @@ -19,6 +19,8 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; [Export(typeof(IEditAndContinueLogReporter))] internal sealed class EditAndContinueLogReporter : IEditAndContinueLogReporter { + private const string CategoryName = "Roslyn"; + private readonly AsyncBatchingWorkQueue _queue; [ImportingConstructor] @@ -59,6 +61,6 @@ public void Report(string message, LogMessageSeverity severity) _ => throw ExceptionUtilities.UnexpectedValue(severity), }; - _queue.AddWork(new HotReloadLogMessage(verbosity, message, errorLevel: errorLevel)); + _queue.AddWork(new HotReloadLogMessage(verbosity, message, errorLevel: errorLevel, category: CategoryName)); } } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs index d606b894ff8..a0f6393b45d 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ArgumentSyntaxExtensions.cs @@ -60,7 +60,7 @@ public static RefKind GetRefKind(this ArgumentSyntax? argument) var parameters = symbol.GetParameters(); // Handle named argument - if (argument.NameColon != null && !argument.NameColon.IsMissing) + if (argument.NameColon is { IsMissing: false }) { var name = argument.NameColon.Name.Identifier.ValueText; var parameter = parameters.FirstOrDefault(p => p.Name == name); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/AttributeArgumentSyntaxExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/AttributeArgumentSyntaxExtensions.cs index 68ffd987135..aef84a2bd23 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/AttributeArgumentSyntaxExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/AttributeArgumentSyntaxExtensions.cs @@ -45,7 +45,7 @@ internal static class AttributeArgumentSyntaxExtensions var parameters = symbol.GetParameters(); // Handle named argument - if (argument.NameColon != null && !argument.NameColon.IsMissing) + if (argument.NameColon is { IsMissing: false }) { var name = argument.NameColon.Name.Identifier.ValueText; var parameter = parameters.FirstOrDefault(p => p.Name == name); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ConversionExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ConversionExtensions.cs index 4324a90e642..51f3d04c5ad 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ConversionExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ConversionExtensions.cs @@ -9,14 +9,9 @@ internal static class ConversionExtensions public static bool IsIdentityOrImplicitReference(this Conversion conversion) { return conversion.IsIdentity || - (conversion.IsImplicit && conversion.IsReference); + (conversion is { IsImplicit: true, IsReference: true }); } public static bool IsImplicitUserDefinedConversion(this Conversion conversion) - { - return conversion.IsUserDefined && - conversion.MethodSymbol != null && - conversion.MethodSymbol.MethodKind == MethodKind.Conversion && - conversion.MethodSymbol.Name == "op_Implicit"; - } + => conversion is { IsUserDefined: true, MethodSymbol: { MethodKind: MethodKind.Conversion, Name: "op_Implicit" } }; } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 57abe1d1c85..d3af501f837 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -657,12 +657,12 @@ public static bool IsNameOfInvocation(this InvocationExpressionSyntax invocation public static SimpleNameSyntax? GetRightmostName(this ExpressionSyntax node) { - if (node is MemberAccessExpressionSyntax memberAccess && memberAccess.Name != null) + if (node is MemberAccessExpressionSyntax { Name: not null } memberAccess) { return memberAccess.Name; } - if (node is QualifiedNameSyntax qualified && qualified.Right != null) + if (node is QualifiedNameSyntax { Right: not null } qualified) { return qualified.Right; } @@ -682,7 +682,7 @@ public static bool IsNameOfInvocation(this InvocationExpressionSyntax invocation return memberBinding.Name; } - if (node is AliasQualifiedNameSyntax aliasQualifiedName && aliasQualifiedName.Name != null) + if (node is AliasQualifiedNameSyntax { Name: not null } aliasQualifiedName) { return aliasQualifiedName.Name; } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SemanticModelExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SemanticModelExtensions.cs index bd3ee7309fc..fb61e742c5c 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SemanticModelExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SemanticModelExtensions.cs @@ -204,8 +204,7 @@ public static Accessibility DetermineAccessibilityConstraint( // as the interface type itself. if (type != null) { - if (type.Parent is BaseTypeSyntax baseType && - baseType.Parent is BaseListSyntax baseList && + if (type.Parent is BaseTypeSyntax { Parent: BaseListSyntax baseList } baseType && baseType.Type == type) { var containingType = semanticModel.GetDeclaredSymbol(type.GetAncestor(), cancellationToken); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs index cb6f346f48e..00f5ce10d0f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs @@ -823,9 +823,8 @@ static bool TakesExpressionTree(SymbolInfo info, INamedTypeSymbol expressionType { foreach (var symbol in info.GetAllSymbols()) { - if (symbol is IMethodSymbol method && - method.Parameters.Length > 0 && - expressionType.Equals(method.Parameters[0].Type?.OriginalDefinition)) + if (symbol is IMethodSymbol { Parameters: [{ Type.OriginalDefinition: var parameterType }, ..] } && + expressionType.Equals(parameterType)) { return true; } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs index 25da07f98c4..97e48baea46 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs @@ -87,14 +87,7 @@ public override TriviaData Create(SyntaxToken token1, SyntaxToken token2) } private static bool ContainsOnlyWhitespace(Analyzer.AnalysisResult result) - { - return - !result.HasComments && - !result.HasPreprocessor && - !result.HasSkippedTokens && - !result.HasSkippedOrDisabledText && - !result.HasConflictMarker; - } + => result is { HasComments: false, HasPreprocessor: false, HasSkippedTokens: false, HasSkippedOrDisabledText: false, HasConflictMarker: false }; private TriviaData? GetWhitespaceOnlyTriviaInfo(SyntaxToken token1, SyntaxToken token2, Analyzer.AnalysisResult result) { @@ -114,7 +107,7 @@ private static bool ContainsOnlyWhitespace(Analyzer.AnalysisResult result) } // tab is used in a place where it is not an indentation - if (result.LineBreaks == 0 && result.Tab > 0) + if (result is { LineBreaks: 0, Tab: > 0 }) { // calculate actual space size from tab var spaces = CalculateSpaces(token1, token2); @@ -143,7 +136,7 @@ private int CalculateSpaces(SyntaxToken token1, SyntaxToken token2) var indentation = result.Tab * Options.TabSize + result.Space; if (result.HasTrailingSpace || result.HasUnknownWhitespace) { - if (result.HasUnknownWhitespace && result.LineBreaks == 0 && indentation == 0) + if (result is { HasUnknownWhitespace: true, LineBreaks: 0 } && indentation == 0) { // make sure we don't remove all whitespace indentation = 1; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs index b0617b818ea..bda857f80c8 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs @@ -371,11 +371,7 @@ FixedStatementSyntax or } public static bool IsNestedQueryExpression(this SyntaxToken token) - { - return token.Kind() == SyntaxKind.InKeyword && - token.Parent is FromClauseSyntax fromClause && - fromClause.Expression is QueryExpressionSyntax; - } + => token.Kind() == SyntaxKind.InKeyword && token.Parent is FromClauseSyntax { Expression: QueryExpressionSyntax }; public static bool IsFirstFromKeywordInExpression(this SyntaxToken token) { @@ -525,9 +521,7 @@ public static bool IsInterpolation(this SyntaxToken currentToken) /// public static bool IsOpenParenInVarDeconstructionDeclaration(this SyntaxToken currentToken) { - return currentToken.Kind() == SyntaxKind.OpenParenToken && - currentToken.Parent is ParenthesizedVariableDesignationSyntax && - currentToken.Parent.Parent is DeclarationExpressionSyntax; + return currentToken.Kind() == SyntaxKind.OpenParenToken && currentToken is { Parent: ParenthesizedVariableDesignationSyntax, Parent.Parent: DeclarationExpressionSyntax }; } /// diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs index 51de5d2387a..be2efa19a72 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs @@ -40,7 +40,7 @@ public override void AddSuppressOperations(ArrayBuilder list, private static void AddPropertyDeclarationSuppressOperations(ArrayBuilder list, SyntaxNode node) { - if (node is BasePropertyDeclarationSyntax basePropertyDeclaration && basePropertyDeclaration.AccessorList != null && + if (node is BasePropertyDeclarationSyntax { AccessorList: not null } basePropertyDeclaration && basePropertyDeclaration.AccessorList.Accessors.All(a => a.Body == null) && basePropertyDeclaration.GetAnnotatedTrivia(SyntaxAnnotation.ElasticAnnotation).Any()) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs index 81012f44149..33ca37590e8 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs @@ -79,8 +79,7 @@ private void AddSwitchIndentationOperation(List list, Synt } // can this ever happen? - if (section.Labels.Count == 0 && - section.Statements.Count == 0) + if (section is { Labels.Count: 0, Statements.Count: 0 }) { return; } @@ -160,22 +159,22 @@ private static void AddAlignmentBlockOperation(List list, case AnonymousMethodExpressionSyntax anonymousMethod: SetAlignmentBlockOperation(list, anonymousMethod, anonymousMethod.Block); return; - case BaseObjectCreationExpressionSyntax objectCreation when objectCreation.Initializer != null: + case BaseObjectCreationExpressionSyntax { Initializer: not null } objectCreation: SetAlignmentBlockOperation(list, objectCreation, objectCreation.Initializer); return; case AnonymousObjectCreationExpressionSyntax anonymousObjectCreation: SetAlignmentBlockOperation(list, anonymousObjectCreation.NewKeyword, anonymousObjectCreation.OpenBraceToken, anonymousObjectCreation.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine); return; - case ArrayCreationExpressionSyntax arrayCreation when arrayCreation.Initializer != null: + case ArrayCreationExpressionSyntax { Initializer: not null } arrayCreation: SetAlignmentBlockOperation(list, arrayCreation.NewKeyword, arrayCreation.Initializer.OpenBraceToken, arrayCreation.Initializer.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine); return; - case ImplicitArrayCreationExpressionSyntax implicitArrayCreation when implicitArrayCreation.Initializer != null: + case ImplicitArrayCreationExpressionSyntax { Initializer: not null } implicitArrayCreation: SetAlignmentBlockOperation(list, implicitArrayCreation.NewKeyword, implicitArrayCreation.Initializer.OpenBraceToken, implicitArrayCreation.Initializer.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine); return; - case StackAllocArrayCreationExpressionSyntax arrayCreation when arrayCreation.Initializer != null: + case StackAllocArrayCreationExpressionSyntax { Initializer: not null } arrayCreation: SetAlignmentBlockOperation(list, arrayCreation.StackAllocKeyword, arrayCreation.Initializer.OpenBraceToken, arrayCreation.Initializer.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine); return; - case ImplicitStackAllocArrayCreationExpressionSyntax implicitArrayCreation when implicitArrayCreation.Initializer != null: + case ImplicitStackAllocArrayCreationExpressionSyntax { Initializer: not null } implicitArrayCreation: SetAlignmentBlockOperation(list, implicitArrayCreation.StackAllocKeyword, implicitArrayCreation.Initializer.OpenBraceToken, implicitArrayCreation.Initializer.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine); return; case SwitchExpressionSyntax switchExpression: @@ -294,13 +293,13 @@ private static void AddAlignmentBlockOperationRelativeToFirstTokenOnBaseTokenLin private static void AddEmbeddedStatementsIndentationOperation(List list, SyntaxNode node) { // increase indentation - embedded statement cases - if (node is IfStatementSyntax ifStatement && ifStatement.Statement != null && !(ifStatement.Statement is BlockSyntax)) + if (node is IfStatementSyntax { Statement: not null } ifStatement && !(ifStatement.Statement is BlockSyntax)) { AddEmbeddedStatementsIndentationOperation(list, ifStatement.Statement); return; } - if (node is ElseClauseSyntax elseClause && elseClause.Statement != null) + if (node is ElseClauseSyntax { Statement: not null } elseClause) { if (elseClause.Statement is not (BlockSyntax or IfStatementSyntax)) { @@ -310,43 +309,43 @@ private static void AddEmbeddedStatementsIndentationOperation(List list, } private static bool IsEmptyForStatement(ForStatementSyntax forStatement) - => forStatement.Initializers.Count == 0 - && forStatement.Declaration == null - && forStatement.Condition == null - && forStatement.Incrementors.Count == 0; + => forStatement is { Initializers.Count: 0, Declaration: null, Condition: null, Incrementors.Count: 0 }; private void SuppressVariableDeclaration(ArrayBuilder list, SyntaxNode node) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs index a4e91ffcd8e..b8b1af6192f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs @@ -167,7 +167,7 @@ private static void AddSpecificNodesSuppressOperations(ArrayBuilder node is DeclarationExpressionSyntax; public bool IsNamedArgument([NotNullWhen(true)] SyntaxNode? node) - => node is ArgumentSyntax arg && arg.NameColon != null; + => node is ArgumentSyntax { NameColon: not null }; public bool IsNameOfNamedArgument([NotNullWhen(true)] SyntaxNode? node) => node.CheckParent(p => p.Name == node); @@ -201,7 +201,7 @@ public bool IsUsingDirectiveName([NotNullWhen(true)] SyntaxNode? node) usingDirective.NamespaceOrType == node; public bool IsUsingAliasDirective([NotNullWhen(true)] SyntaxNode? node) - => node is UsingDirectiveSyntax usingDirectiveNode && usingDirectiveNode.Alias != null; + => node is UsingDirectiveSyntax { Alias: not null }; public void GetPartsOfUsingAliasDirective(SyntaxNode node, out SyntaxToken globalKeyword, out SyntaxToken alias, out SyntaxNode name) { @@ -268,6 +268,9 @@ public bool IsBaseConstructorInitializer(SyntaxToken token) => token.Parent is ConstructorInitializerSyntax(SyntaxKind.BaseConstructorInitializer) constructorInit && constructorInit.ThisOrBaseKeyword == token; + public bool HasImplicitBaseConstructorInitializer(SyntaxNode node) + => node is ConstructorDeclarationSyntax constructor && constructor.Initializer == null; + public bool IsQueryKeyword(SyntaxToken token) { switch (token.Kind()) @@ -887,8 +890,14 @@ void AppendParameterList(StringBuilder builder, ParameterListSyntax? parameterLi #endif } - public SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration) - => ((TypeDeclarationSyntax)typeDeclaration).Members; + public SyntaxList GetMembersOfTypeDeclaration(SyntaxNode node) + => node is TypeDeclarationSyntax { Members: var members } ? members : []; + + public SyntaxList GetMembersOfNamespaceDeclaration(SyntaxNode node) + => node is BaseNamespaceDeclarationSyntax { Members: var members } ? members : []; + + public SyntaxList GetMembersOfCompilationUnit(SyntaxNode node) + => node is CompilationUnitSyntax { Members: var members } ? members : []; protected override void AppendMembers(SyntaxNode? node, ArrayBuilder list, bool topLevel, bool methodLevel) { @@ -993,42 +1002,6 @@ public TextSpan GetMemberBodySpanForSpeculativeBinding(SyntaxNode node) return node is PatternSyntax ? null : node; } - public IEnumerable GetConstructors(SyntaxNode? root, CancellationToken cancellationToken) - { - if (root is not CompilationUnitSyntax compilationUnit) - return []; - - var constructors = new List(); - AppendConstructors(compilationUnit.Members, constructors, cancellationToken); - return constructors; - } - - private static void AppendConstructors(SyntaxList members, List constructors, CancellationToken cancellationToken) - { - foreach (var member in members) - { - cancellationToken.ThrowIfCancellationRequested(); - switch (member) - { - case ConstructorDeclarationSyntax constructor: - constructors.Add(constructor); - continue; - case BaseNamespaceDeclarationSyntax @namespace: - AppendConstructors(@namespace.Members, constructors, cancellationToken); - break; - case ClassDeclarationSyntax @class: - AppendConstructors(@class.Members, constructors, cancellationToken); - break; - case RecordDeclarationSyntax record: - AppendConstructors(record.Members, constructors, cancellationToken); - break; - case StructDeclarationSyntax @struct: - AppendConstructors(@struct.Members, constructors, cancellationToken); - break; - } - } - } - public TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { var trivia = syntaxTree.GetRoot(cancellationToken).FindTrivia(position, findInsideTrivia: false); @@ -1104,8 +1077,7 @@ public SyntaxNode GetRightHandSideOfAssignment(SyntaxNode node) => ((AssignmentExpressionSyntax)node).Right; public bool IsInferredAnonymousObjectMemberDeclarator([NotNullWhen(true)] SyntaxNode? node) - => node is AnonymousObjectMemberDeclaratorSyntax anonObject && - anonObject.NameEquals == null; + => node is AnonymousObjectMemberDeclaratorSyntax { NameEquals: null }; public bool IsOperandOfIncrementExpression([NotNullWhen(true)] SyntaxNode? node) => node?.Parent?.Kind() is SyntaxKind.PostIncrementExpression or SyntaxKind.PreIncrementExpression; @@ -1363,8 +1335,7 @@ public SyntaxList GetAttributeLists(SyntaxNode? node) => node.GetAttributeLists(); public bool IsParameterNameXmlElementSyntax([NotNullWhen(true)] SyntaxNode? node) - => node is XmlElementSyntax xmlElement && - xmlElement.StartTag.Name.LocalName.ValueText == DocumentationCommentXmlNames.ParameterElementName; + => node is XmlElementSyntax { StartTag.Name.LocalName.ValueText: DocumentationCommentXmlNames.ParameterElementName }; public SyntaxList GetContentFromDocumentationCommentTriviaSyntax(SyntaxTrivia trivia) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index e74bd19a931..79e72209131 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -176,6 +176,7 @@ public int Convert(TSyntaxKind kind) where TSyntaxKind : struct public int ElseClause => (int)SyntaxKind.ElseClause; public int EqualsValueClause => (int)SyntaxKind.EqualsValueClause; + public int? ExpressionElement => (int)SyntaxKind.ExpressionElement; public int? ImplicitElementAccess => (int)SyntaxKind.ImplicitElementAccess; public int Interpolation => (int)SyntaxKind.Interpolation; public int InterpolatedStringExpression => (int)SyntaxKind.InterpolatedStringExpression; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs index 81b05d4148b..fa0e5e0a114 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs @@ -918,7 +918,7 @@ private static bool IsSwitchOrConditionalCastSafeToRemove operation.ConstantValue.HasValue && operation.ConstantValue.Value is null; + => operation.ConstantValue is { HasValue: true, Value: null }; private static bool IsExplicitCast(SyntaxNode node) => node is ExpressionSyntax expression && expression.WalkDownParentheses().Kind() is SyntaxKind.CastExpression or SyntaxKind.AsExpression; @@ -1204,7 +1204,7 @@ private static bool IsIdentityFloatingPointCastThatMustBePreserved( // 64bit location. As such, the explicit cast to truncate to 32/64 isn't necessary. See // https://github.com/dotnet/roslyn/pull/56932#discussion_r725241921 for more details. var parentConversion = semanticModel.GetConversion(castNode, cancellationToken); - if (parentConversion.Exists && parentConversion.IsBoxing) + if (parentConversion is { Exists: true, IsBoxing: true }) return false; // It wasn't a read from a fp/field/array. But it might be a write into one. @@ -1335,8 +1335,7 @@ private static bool ChangedOverloadResolution( // ignore local functions. First, we can't test them for equality in speculative situations, but also we // can't end up with an overload resolution issue for them as they don't have overloads. - if (oldSymbolInfo is IMethodSymbol method && - method.MethodKind is not (MethodKind.LocalFunction or MethodKind.LambdaMethod) && + if (oldSymbolInfo is IMethodSymbol { MethodKind: not (MethodKind.LocalFunction or MethodKind.LambdaMethod) } && !Equals(oldSymbolInfo, newSymbolInfo)) { return true; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/NameSyntaxComparer.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/NameSyntaxComparer.cs index 541f53c67b5..d56e984dcc2 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/NameSyntaxComparer.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/NameSyntaxComparer.cs @@ -40,12 +40,10 @@ public int Compare(NameSyntax? x, NameSyntax? y) (IdentifierNameSyntax identifierX, IdentifierNameSyntax identifierY) => _tokenComparer.Compare(identifierX.Identifier, identifierY.Identifier), (GenericNameSyntax genericX, GenericNameSyntax genericY) => Compare(genericX, genericY), (IdentifierNameSyntax identifierX, GenericNameSyntax genericY) => - _tokenComparer.Compare(identifierX.Identifier, genericY.Identifier) is var diff && diff != 0 - ? diff + _tokenComparer.Compare(identifierX.Identifier, genericY.Identifier) is var diff and not 0 ? diff : -1, // Goo goes before Goo (GenericNameSyntax genericX, IdentifierNameSyntax identifierY) => - _tokenComparer.Compare(genericX.Identifier, identifierY.Identifier) is var diff && diff != 0 - ? diff + _tokenComparer.Compare(genericX.Identifier, identifierY.Identifier) is var diff and not 0 ? diff : -1, // Goo goes after Goo (_, _) => DecomposeCompare(x, y), }; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs index 6f5bee5068d..ee47ba32db5 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs @@ -663,7 +663,7 @@ protected override bool IsParenthesizedExpression(SyntaxNode node) => node.IsKind(SyntaxKind.ParenthesizedExpression); protected override bool IsNamedArgument(ArgumentSyntax argument) - => argument.NameColon != null && !argument.NameColon.IsMissing; + => argument.NameColon is { IsMissing: false }; protected override string GetNamedArgumentIdentifierValueText(ArgumentSyntax argument) => argument.NameColon.Name.Identifier.ValueText; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs index c15705abd5b..b70af15c89d 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs @@ -15,6 +15,13 @@ namespace Microsoft.CodeAnalysis.CSharp.Utilities; internal partial class CSharpTypeStyleHelper { + public enum Context + { + BuiltInType, + TypeIsApparent, + Elsewhere + } + protected readonly struct State { public readonly UseVarPreference TypeStylePreference; @@ -23,16 +30,13 @@ protected readonly struct State private readonly NotificationOption2 _whenTypeIsApparent; private readonly NotificationOption2 _elsewhere; - public readonly bool IsInIntrinsicTypeContext; - public readonly bool IsTypeApparentInContext; + public readonly Context Context; public State( SyntaxNode declaration, SemanticModel semanticModel, CSharpSimplifierOptions options, CancellationToken cancellationToken) { TypeStylePreference = default; - IsInIntrinsicTypeContext = default; - IsTypeApparentInContext = default; var styleForIntrinsicTypes = options.VarForBuiltInTypes; var styleForApparent = options.VarWhenTypeIsApparent; @@ -44,18 +48,30 @@ public State( this.TypeStylePreference = options.GetUseVarPreference(); - IsTypeApparentInContext = - declaration is VariableDeclarationSyntax varDecl - && IsTypeApparentInDeclaration(varDecl, semanticModel, TypeStylePreference, cancellationToken); - - IsInIntrinsicTypeContext = - IsPredefinedTypeInDeclaration(declaration, semanticModel) - || IsInferredPredefinedType(declaration, semanticModel); + if (IsPredefinedTypeInDeclaration(declaration, semanticModel) || + IsInferredPredefinedType(declaration, semanticModel)) + { + this.Context = Context.BuiltInType; + } + else if (declaration is VariableDeclarationSyntax varDecl && + IsTypeApparentInDeclaration(varDecl, semanticModel, TypeStylePreference, cancellationToken)) + { + this.Context = Context.TypeIsApparent; + } + else + { + this.Context = Context.Elsewhere; + } } public NotificationOption2 GetDiagnosticSeverityPreference() - => IsInIntrinsicTypeContext ? _forBuiltInTypes : - IsTypeApparentInContext ? _whenTypeIsApparent : _elsewhere; + => Context switch + { + Context.BuiltInType => _forBuiltInTypes, + Context.TypeIsApparent => _whenTypeIsApparent, + Context.Elsewhere => _elsewhere, + _ => throw ExceptionUtilities.UnexpectedValue(Context), + }; /// /// Returns true if type information could be gleaned by simply looking at the given statement. diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs index 2133f041888..737932bb48d 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs @@ -11,44 +11,21 @@ namespace Microsoft.CodeAnalysis.CSharp.Utilities; -internal readonly struct TypeStyleResult -{ - private readonly CSharpTypeStyleHelper _helper; - private readonly TypeSyntax _typeName; - private readonly SemanticModel _semanticModel; - private readonly CSharpSimplifierOptions _options; - private readonly CancellationToken _cancellationToken; - - /// - /// Whether or not converting would transition the code to the style the user prefers. i.e. if the user likes - /// var for everything, and you have int i = 0 then will be - /// . However, if the user likes var for everything and you have var i = 0, - /// then it's still possible to convert that, it would just be for - /// because it goes against the user's preferences. - /// - /// - /// In general, most features should only convert the type if is - /// . The one exception is the refactoring, which is explicitly there to still let people - /// convert things quickly, even if it's going against their stated style. - /// - public readonly bool IsStylePreferred; - public readonly NotificationOption2 Notification; - - public TypeStyleResult(CSharpTypeStyleHelper helper, TypeSyntax typeName, SemanticModel semanticModel, CSharpSimplifierOptions options, bool isStylePreferred, NotificationOption2 notificationOption, CancellationToken cancellationToken) : this() - { - _helper = helper; - _typeName = typeName; - _semanticModel = semanticModel; - _options = options; - _cancellationToken = cancellationToken; - - IsStylePreferred = isStylePreferred; - Notification = notificationOption; - } - - public bool CanConvert() - => _helper.TryAnalyzeVariableDeclaration(_typeName, _semanticModel, _options, _cancellationToken); -} +/// +/// Whether or not converting would transition the code to the style the user prefers. i.e. if the user likes +/// var for everything, and you have int i = 0 then will be +/// . However, if the user likes var for everything and you have var i = 0, +/// then it's still possible to convert that, it would just be for +/// because it goes against the user's preferences. +/// In general, most features should only convert the type if is +/// . The one exception is the refactoring, which is explicitly there to still let people +/// convert things quickly, even if it's going against their stated style. +/// +internal readonly record struct TypeStyleResult( + bool CanConvert, + CSharpTypeStyleHelper.Context Context, + bool IsStylePreferred, + NotificationOption2 Notification); internal abstract partial class CSharpTypeStyleHelper { @@ -59,17 +36,18 @@ public virtual TypeStyleResult AnalyzeTypeName( CSharpSimplifierOptions options, CancellationToken cancellationToken) { if (typeName?.FirstAncestorOrSelf(a => a.Kind() is SyntaxKind.DeclarationExpression or SyntaxKind.VariableDeclaration or SyntaxKind.ForEachStatement) is not { } declaration) - { return default; - } var state = new State( declaration, semanticModel, options, cancellationToken); var isStylePreferred = this.IsStylePreferred(in state); var notificationOption = state.GetDiagnosticSeverityPreference(); + var canConvert = this.TryAnalyzeVariableDeclaration( + typeName, semanticModel, options, cancellationToken); + return new TypeStyleResult( - this, typeName, semanticModel, options, isStylePreferred, notificationOption, cancellationToken); + canConvert, state.Context, isStylePreferred, notificationOption); } internal abstract bool TryAnalyzeVariableDeclaration( diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs index b93ca40fcf8..cce3840ecc0 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs @@ -23,19 +23,13 @@ private CSharpUseExplicitTypeHelper() protected override bool IsStylePreferred(in State state) { var stylePreferences = state.TypeStylePreference; - - if (state.IsInIntrinsicTypeContext) - { - return !stylePreferences.HasFlag(UseVarPreference.ForBuiltInTypes); - } - else if (state.IsTypeApparentInContext) + return state.Context switch { - return !stylePreferences.HasFlag(UseVarPreference.WhenTypeIsApparent); - } - else - { - return !stylePreferences.HasFlag(UseVarPreference.Elsewhere); - } + Context.BuiltInType => !stylePreferences.HasFlag(UseVarPreference.ForBuiltInTypes), + Context.TypeIsApparent => !stylePreferences.HasFlag(UseVarPreference.WhenTypeIsApparent), + Context.Elsewhere => !stylePreferences.HasFlag(UseVarPreference.Elsewhere), + _ => throw ExceptionUtilities.UnexpectedValue(state.Context), + }; } public override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, CancellationToken cancellationToken) @@ -81,8 +75,7 @@ internal override bool TryAnalyzeVariableDeclaration( return false; } - if (typeName.Parent is VariableDeclarationSyntax variableDeclaration && - typeName.Parent.Parent is (kind: SyntaxKind.LocalDeclarationStatement or SyntaxKind.ForStatement or SyntaxKind.UsingStatement)) + if (typeName is { Parent: VariableDeclarationSyntax variableDeclaration, Parent.Parent: (kind: SyntaxKind.LocalDeclarationStatement or SyntaxKind.ForStatement or SyntaxKind.UsingStatement) }) { // check assignment for variable declarations. var variable = variableDeclaration.Variables.First(); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs index e37d16980e7..30fa8aef0d3 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseImplicitTypeHelper.cs @@ -79,19 +79,13 @@ protected override bool ShouldAnalyzeDeclarationExpression(DeclarationExpression protected override bool IsStylePreferred(in State state) { var stylePreferences = state.TypeStylePreference; - - if (state.IsInIntrinsicTypeContext) - { - return stylePreferences.HasFlag(UseVarPreference.ForBuiltInTypes); - } - else if (state.IsTypeApparentInContext) + return state.Context switch { - return stylePreferences.HasFlag(UseVarPreference.WhenTypeIsApparent); - } - else - { - return stylePreferences.HasFlag(UseVarPreference.Elsewhere); - } + Context.BuiltInType => stylePreferences.HasFlag(UseVarPreference.ForBuiltInTypes), + Context.TypeIsApparent => stylePreferences.HasFlag(UseVarPreference.WhenTypeIsApparent), + Context.Elsewhere => stylePreferences.HasFlag(UseVarPreference.Elsewhere), + _ => throw ExceptionUtilities.UnexpectedValue(state.Context), + }; } internal override bool TryAnalyzeVariableDeclaration( @@ -109,11 +103,7 @@ internal override bool TryAnalyzeVariableDeclaration( return false; } - if (typeName.Parent is VariableDeclarationSyntax variableDeclaration && - typeName.Parent.Parent is (kind: - SyntaxKind.LocalDeclarationStatement or - SyntaxKind.ForStatement or - SyntaxKind.UsingStatement)) + if (typeName is { Parent: VariableDeclarationSyntax variableDeclaration, Parent.Parent: (kind: SyntaxKind.LocalDeclarationStatement or SyntaxKind.ForStatement or SyntaxKind.UsingStatement) }) { // implicitly typed variables cannot be constants. if (variableDeclaration.Parent is LocalDeclarationStatementSyntax { IsConst: true }) diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 61cc4c1c411..84684fb467f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -201,6 +201,7 @@ + @@ -543,7 +544,6 @@ - diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/KnownTypes.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/KnownTypes.cs similarity index 100% rename from src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/KnownTypes.cs rename to src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/KnownTypes.cs diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolDisplayPartExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolDisplayPartExtensions.cs index b52deb8b4fc..17060dc9890 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolDisplayPartExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SymbolDisplayPartExtensions.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Shared.Extensions; @@ -15,7 +16,7 @@ public static string GetFullText(this IEnumerable parts) public static void AddLineBreak(this IList parts, string text = "\r\n") => parts.Add(new SymbolDisplayPart(SymbolDisplayPartKind.LineBreak, null, text)); - public static void AddMethodName(this IList parts, string text) + public static void AddMethodName(this ArrayBuilder parts, string text) => parts.Add(new SymbolDisplayPart(SymbolDisplayPartKind.MethodName, null, text)); public static void AddPunctuation(this IList parts, string text) diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs index 97ab1349215..fa5d00eb49c 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs @@ -222,7 +222,7 @@ public BasicBlockAnalysisData GetOrCreateBlockAnalysisData(BasicBlock basicBlock // Filter down the operations to writes within this block range. writesInBlockRange = PooledHashSet<(ISymbol, IOperation)>.GetInstance(); - foreach (var (symbol, write) in SymbolsWriteBuilder.Where(kvp => !kvp.Value).Select(kvp => kvp.Key).ToArray()) + foreach (var (symbol, write) in SymbolsWriteBuilder.SelectAsArray(kvp => !kvp.Value, kvp => kvp.Key)) { if (write != null && operations.Contains(write)) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs index e0fcc56a680..44d79372c4f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs @@ -44,8 +44,8 @@ public bool HasUnreadSymbolWrites() /// Gets symbol writes that have are never read. /// WriteOperation will be null for the initial value write to parameter symbols from the callsite. /// - public IEnumerable<(ISymbol Symbol, IOperation WriteOperation)> GetUnreadSymbolWrites() - => SymbolWritesMap.Where(kvp => !kvp.Value).Select(kvp => kvp.Key); + public ImmutableArray<(ISymbol Symbol, IOperation WriteOperation)> GetUnreadSymbolWrites() + => SymbolWritesMap.SelectAsArray(kvp => !kvp.Value, kvp => kvp.Key); /// /// Returns true if the initial value of the parameter from the caller is used. diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index d4bfb48e6c1..b67f47b2bee 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -173,7 +173,6 @@ internal interface ISyntaxFacts bool IsGlobalAssemblyAttribute([NotNullWhen(true)] SyntaxNode? node); bool IsGlobalModuleAttribute([NotNullWhen(true)] SyntaxNode? node); bool IsDeclaration([NotNullWhen(true)] SyntaxNode? node); - bool IsTypeDeclaration(SyntaxNode node); bool IsUsingAliasDirective([NotNullWhen(true)] SyntaxNode? node); @@ -365,6 +364,8 @@ void GetPartsOfTupleExpression(SyntaxNode node, bool IsThisConstructorInitializer(SyntaxToken token); bool IsBaseConstructorInitializer(SyntaxToken token); + bool HasImplicitBaseConstructorInitializer(SyntaxNode constructorDeclaration); + bool IsQueryKeyword(SyntaxToken token); bool IsElementAccessExpression([NotNullWhen(true)] SyntaxNode? node); bool IsIdentifierStartCharacter(char c); @@ -418,6 +419,8 @@ void GetPartsOfTupleExpression(SyntaxNode node, // Violation. This is a feature level API. void AddMethodLevelMembers(SyntaxNode? root, ArrayBuilder result); + SyntaxList GetMembersOfCompilationUnit(SyntaxNode typeDeclaration); + SyntaxList GetMembersOfNamespaceDeclaration(SyntaxNode typeDeclaration); SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration); // Violation. This is a feature level API. @@ -440,9 +443,6 @@ void GetPartsOfTupleExpression(SyntaxNode node, // Violation. This is a feature level API. SyntaxNode? TryGetBindableParent(SyntaxToken token); - // Violation. This is a feature level API. - IEnumerable GetConstructors(SyntaxNode? root, CancellationToken cancellationToken); - /// /// Given a , that represents and argument return the string representation of /// that arguments name. @@ -498,6 +498,7 @@ void GetPartsOfTupleExpression(SyntaxNode node, bool IsAnonymousFunctionExpression([NotNullWhen(true)] SyntaxNode? node); bool IsBaseNamespaceDeclaration([NotNullWhen(true)] SyntaxNode? node); + bool IsTypeDeclaration(SyntaxNode node); bool IsBinaryExpression([NotNullWhen(true)] SyntaxNode? node); bool IsLiteralExpression([NotNullWhen(true)] SyntaxNode? node); bool IsMemberAccessExpression([NotNullWhen(true)] SyntaxNode? node); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index fbb47a46606..9c8859760a0 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -709,7 +709,7 @@ public static bool IsWhitespaceTrivia(this ISyntaxFacts syntaxFacts, SyntaxTrivi => trivia.RawKind == syntaxFacts.SyntaxKinds.WhitespaceTrivia; public static bool IsSkippedTokensTrivia(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.SkippedTokensTrivia; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.SkippedTokensTrivia; #endregion @@ -749,136 +749,136 @@ public static bool IsInterpolatedStringTextToken(this ISyntaxFacts syntaxFacts, #region names public static bool IsAliasQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.AliasQualifiedName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.AliasQualifiedName; public static bool IsGenericName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.GenericName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.GenericName; public static bool IsIdentifierName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IdentifierName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IdentifierName; public static bool IsQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.QualifiedName; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.QualifiedName; #endregion #region types public static bool IsTupleType(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TupleType; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TupleType; #endregion #region literal expressions public static bool IsCharacterLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.CharacterLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.CharacterLiteralExpression; public static bool IsDefaultLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.DefaultLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.DefaultLiteralExpression; public static bool IsFalseLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.FalseLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.FalseLiteralExpression; public static bool IsNumericLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.NumericLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.NumericLiteralExpression; public static bool IsNullLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.NullLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.NullLiteralExpression; public static bool IsStringLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.StringLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.StringLiteralExpression; public static bool IsTrueLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TrueLiteralExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TrueLiteralExpression; #endregion #region expressions public static bool IsArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ArrayCreationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ArrayCreationExpression; public static bool IsAwaitExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.AwaitExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.AwaitExpression; public static bool IsBaseExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.BaseExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.BaseExpression; public static bool IsConditionalExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConditionalExpression; public static bool IsConditionalAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalAccessExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConditionalAccessExpression; public static bool IsFieldExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.FieldExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.FieldExpression; public static bool IsImplicitArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitArrayCreationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitArrayCreationExpression; public static bool IsImplicitObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitObjectCreationExpression; public static bool IsIndexExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IndexExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IndexExpression; public static bool IsInterpolatedStringExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringExpression; public static bool IsInterpolation(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.Interpolation; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.Interpolation; public static bool IsInterpolatedStringText(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringText; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringText; public static bool IsInvocationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InvocationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InvocationExpression; public static bool IsIsTypeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IsTypeExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IsTypeExpression; public static bool IsIsNotTypeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IsNotTypeExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IsNotTypeExpression; public static bool IsIsPatternExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IsPatternExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IsPatternExpression; public static bool IsLogicalAndExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalAndExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LogicalAndExpression; public static bool IsLogicalOrExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalOrExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LogicalOrExpression; public static bool IsLogicalNotExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LogicalNotExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LogicalNotExpression; public static bool IsObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ObjectCreationExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ObjectCreationExpression; public static bool IsParenthesizedExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedExpression; public static bool IsQueryExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.QueryExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.QueryExpression; public static bool IsRangeExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RangeExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RangeExpression; public static bool IsRefExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RefExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RefExpression; public static bool IsSimpleMemberAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.SimpleMemberAccessExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.SimpleMemberAccessExpression; public static bool IsThisExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ThisExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThisExpression; public static bool IsThrowExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThrowExpression; public static bool IsTupleExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TupleExpression; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TupleExpression; public static bool ContainsGlobalStatement(this ISyntaxFacts syntaxFacts, SyntaxNode node) => node.ChildNodes().Any(c => c.RawKind == syntaxFacts.SyntaxKinds.GlobalStatement); @@ -888,142 +888,145 @@ public static bool ContainsGlobalStatement(this ISyntaxFacts syntaxFacts, Syntax #region pattern public static bool IsAndPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.AndPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.AndPattern; public static bool IsConstantPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConstantPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConstantPattern; public static bool IsDeclarationPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.DeclarationPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.DeclarationPattern; public static bool IsListPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ListPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ListPattern; public static bool IsNotPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.NotPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.NotPattern; public static bool IsOrPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.OrPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.OrPattern; public static bool IsParenthesizedPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ParenthesizedPattern; public static bool IsRecursivePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RecursivePattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RecursivePattern; public static bool IsRelationalPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.RelationalPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.RelationalPattern; public static bool IsTypePattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TypePattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TypePattern; public static bool IsVarPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.VarPattern; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.VarPattern; #endregion #region statements public static bool IsExpressionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ExpressionStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ExpressionStatement; public static bool IsForEachStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ForEachStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ForEachStatement; public static bool IsForStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ForStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ForStatement; public static bool IsIfStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IfStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IfStatement; public static bool IsLocalDeclarationStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LocalDeclarationStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LocalDeclarationStatement; public static bool IsLocalFunctionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LocalFunctionStatement; public static bool IsLockStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.LockStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.LockStatement; public static bool IsReturnStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ReturnStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ReturnStatement; public static bool IsThrowStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ThrowStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ThrowStatement; public static bool IsUsingStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.UsingStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.UsingStatement; public static bool IsWhileStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.WhileStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.WhileStatement; public static bool IsYieldReturnStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.YieldReturnStatement; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.YieldReturnStatement; #endregion #region members/declarations public static bool IsAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.Attribute; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.Attribute; public static bool IsClassDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ClassDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ClassDeclaration; public static bool IsConstructorDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ConstructorDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ConstructorDeclaration; public static bool IsEnumDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.EnumDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.EnumDeclaration; public static bool IsGlobalAttribute(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => syntaxFacts.IsGlobalAssemblyAttribute(node) || syntaxFacts.IsGlobalModuleAttribute(node); public static bool IsInterfaceDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.InterfaceDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.InterfaceDeclaration; public static bool IsParameter(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.Parameter; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.Parameter; public static bool IsTypeConstraint(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TypeConstraint; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TypeConstraint; public static bool IsVariableDeclarator(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.VariableDeclarator; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.VariableDeclarator; public static bool IsFieldDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.FieldDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.FieldDeclaration; public static bool IsPropertyDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.PropertyDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.PropertyDeclaration; public static bool IsStructDeclaration(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.StructDeclaration; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.StructDeclaration; public static bool IsTypeArgumentList(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.TypeArgumentList; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.TypeArgumentList; #endregion #region clauses public static bool IsElseClause(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ElseClause; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ElseClause; public static bool IsEqualsValueClause(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.EqualsValueClause; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.EqualsValueClause; #endregion #region other + public static bool IsExpressionElement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ExpressionElement; + public static bool IsImplicitElementAccess(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitElementAccess; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitElementAccess; public static bool IsIndexerMemberCref(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.IndexerMemberCref; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.IndexerMemberCref; public static bool IsPrimaryConstructorBaseType(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) - => node?.RawKind == syntaxFacts.SyntaxKinds.PrimaryConstructorBaseType; + => node != null && node.RawKind == syntaxFacts.SyntaxKinds.PrimaryConstructorBaseType; #endregion diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index 41981d1cb8d..ac40972113d 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -233,6 +233,7 @@ internal interface ISyntaxKinds #region other + int? ExpressionElement { get; } int? ImplicitElementAccess { get; } int Interpolation { get; } int InterpolatedStringText { get; } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs index 1a54b4bb267..f0c8287b169 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/EditDistance.cs @@ -50,7 +50,7 @@ internal readonly struct EditDistance(string text) : IDisposable private readonly char[] _sourceLowerCaseCharacters = ConvertToLowercaseArray(text); private const int PooledArraySize = 512; - private static readonly ObjectPool s_pool = new ObjectPool(() => new char[PooledArraySize]); + private static readonly ObjectPool s_pool = new(() => new char[PooledArraySize]); private static char[] ConvertToLowercaseArray(string text) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IAsyncEnumerableExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IAsyncEnumerableExtensions.cs index 31cf47d9325..193a68266be 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IAsyncEnumerableExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/IAsyncEnumerableExtensions.cs @@ -16,6 +16,7 @@ internal static class AsyncEnumerable { public static readonly IAsyncEnumerable Empty = GetEmptyAsync(); + // Remove after .NET 10, https://github.com/dotnet/roslyn/issues/80198 #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously private static async IAsyncEnumerable GetEmptyAsync() { @@ -24,6 +25,7 @@ private static async IAsyncEnumerable GetEmptyAsync() #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously } + // Remove after .NET 10, https://github.com/dotnet/roslyn/issues/80198 #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously internal static async IAsyncEnumerable SingletonAsync(T value) { diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs index 4fd2ef3ea2e..e04b8e5ba17 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Compilation/CompilationExtensions.cs @@ -123,6 +123,9 @@ public static ImmutableArray GetReferencedAssemblySymbols(this public static INamedTypeSymbol? FormattableStringType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(FormattableString).FullName!); + public static INamedTypeSymbol? IFormattableType(this Compilation compilation) + => compilation.GetTypeByMetadataName(typeof(IFormattable).FullName!); + public static INamedTypeSymbol? EventArgsType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(EventArgs).FullName!); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/INamedTypeSymbolExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/INamedTypeSymbolExtensions.cs index 44270c933b5..f539d52b2ad 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/INamedTypeSymbolExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/INamedTypeSymbolExtensions.cs @@ -30,6 +30,16 @@ public static IEnumerable GetBaseTypesAndThis(this INamedTypeS } } + public static IEnumerable GetContainingTypesAndThis(this INamedTypeSymbol? namedType) + { + var current = namedType; + while (current != null) + { + yield return current; + current = current.ContainingType; + } + } + public static ImmutableArray GetAllTypeParameters(this INamedTypeSymbol? symbol) { var stack = GetContainmentStack(symbol); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs index 04f773413ad..f6305442a52 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs @@ -11,7 +11,6 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime; using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; @@ -272,7 +271,7 @@ public static bool IsEnumMember([NotNullWhen(true)] this ISymbol? symbol) => symbol is { Kind: SymbolKind.Field, ContainingType.TypeKind: TypeKind.Enum }; /// - public static bool IsExtensionMethod(this ISymbol symbol) + public static bool IsExtensionMethod([NotNullWhen(true)] this ISymbol? symbol) => symbol is IMethodSymbol { IsExtensionMethod: true }; public static bool IsLocalFunction([NotNullWhen(true)] this ISymbol? symbol) @@ -841,4 +840,35 @@ public static bool HasAttribute([NotNullWhen(true)] this ISymbol? symbol, [NotNu return symbol.GetAttributes().Any(static (attribute, attributeClass) => attributeClass.Equals(attribute.AttributeClass), attributeClass); } + + public static bool IsClassicOrModernInstanceExtensionMethod( + [NotNullWhen(true)] this ISymbol? symbol) + { + return IsClassicOrModernInstanceExtensionMethod(symbol, out _); + } + + public static bool IsClassicOrModernInstanceExtensionMethod( + [NotNullWhen(true)] this ISymbol? symbol, + [NotNullWhen(true)] out IMethodSymbol? classicExtensionMethod) + { + if (symbol is IMethodSymbol method) + { + if (method.IsExtensionMethod) + { + classicExtensionMethod = method; + return true; + } + +#if !ROSLYN_4_12_OR_LOWER + if (method is { IsStatic: false, AssociatedExtensionImplementation: { } associatedMethod }) + { + classicExtensionMethod = associatedMethod; + return true; + } +#endif + } + + classicExtensionMethod = null; + return false; + } } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/SymbolEquivalenceComparer.EquivalenceVisitor.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/SymbolEquivalenceComparer.EquivalenceVisitor.cs index 95aa5fbf0ff..02576265a5e 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/SymbolEquivalenceComparer.EquivalenceVisitor.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/SymbolEquivalenceComparer.EquivalenceVisitor.cs @@ -174,6 +174,13 @@ private bool MethodsAreEquivalent(IMethodSymbol x, IMethodSymbol y, Dictionary SyntaxFactory.BinaryPattern(SyntaxKind.AndPattern, (PatternSyntax)Parenthesize(left), (PatternSyntax)Parenthesize(right)); + => SyntaxFactory.BinaryPattern(SyntaxKind.AndPattern, (PatternSyntax)ParenthesizeNonSimple(left), (PatternSyntax)ParenthesizeNonSimple(right)); public override SyntaxNode ConstantPattern(SyntaxNode expression) => SyntaxFactory.ConstantPattern((ExpressionSyntax)expression); @@ -225,10 +225,10 @@ public override SyntaxNode GreaterThanEqualsRelationalPattern(SyntaxNode express => SyntaxFactory.RelationalPattern(GreaterThanEqualsToken, (ExpressionSyntax)expression); public override SyntaxNode NotPattern(SyntaxNode pattern) - => SyntaxFactory.UnaryPattern(NotKeyword, (PatternSyntax)Parenthesize(pattern)); + => SyntaxFactory.UnaryPattern(NotKeyword, (PatternSyntax)ParenthesizeNonSimple(pattern)); public override SyntaxNode OrPattern(SyntaxNode left, SyntaxNode right) - => SyntaxFactory.BinaryPattern(SyntaxKind.OrPattern, (PatternSyntax)Parenthesize(left), (PatternSyntax)Parenthesize(right)); + => SyntaxFactory.BinaryPattern(SyntaxKind.OrPattern, (PatternSyntax)ParenthesizeNonSimple(left), (PatternSyntax)ParenthesizeNonSimple(right)); public override SyntaxNode ParenthesizedPattern(SyntaxNode pattern) => Parenthesize(pattern); @@ -237,12 +237,12 @@ public override SyntaxNode TypePattern(SyntaxNode type) => SyntaxFactory.TypePattern((TypeSyntax)type); public override SyntaxNode UnaryPattern(SyntaxToken operatorToken, SyntaxNode pattern) - => SyntaxFactory.UnaryPattern(operatorToken, (PatternSyntax)Parenthesize(pattern)); + => SyntaxFactory.UnaryPattern(operatorToken, (PatternSyntax)ParenthesizeNonSimple(pattern)); #endregion public override SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression) - => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation); + => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)ParenthesizeNonSimple(expression)).WithAdditionalAnnotations(Simplifier.Annotation); public override SyntaxNode DefaultExpression(SyntaxNode type) => SyntaxFactory.DefaultExpression((TypeSyntax)type).WithAdditionalAnnotations(Simplifier.Annotation); @@ -329,11 +329,16 @@ public override SyntaxNode BitwiseOrExpression(SyntaxNode left, SyntaxNode right => CreateBinaryExpression(SyntaxKind.BitwiseOrExpression, left, right); public static SyntaxNode CreateBinaryExpression(SyntaxKind syntaxKind, SyntaxNode left, SyntaxNode right) - => SyntaxFactory.BinaryExpression(syntaxKind, (ExpressionSyntax)Parenthesize(left), (ExpressionSyntax)Parenthesize(right)); + => SyntaxFactory.BinaryExpression(syntaxKind, (ExpressionSyntax)ParenthesizeNonSimple(left), (ExpressionSyntax)ParenthesizeNonSimple(right)); public override SyntaxNode IdentifierName(string identifier) => identifier.ToIdentifierName(); public override SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression) - => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation); + => SyntaxFactory.CastExpression((TypeSyntax)type, (ExpressionSyntax)ParenthesizeNonSimple(expression)).WithAdditionalAnnotations(Simplifier.Annotation); + + internal static SyntaxNode ParenthesizeNonSimple(SyntaxNode expression) + => expression is IdentifierNameSyntax + ? expression + : Parenthesize(expression); } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs index 53142408658..f14bdbbadb4 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/SyntaxEditorBasedCodeFixProvider.cs @@ -102,10 +102,9 @@ protected abstract Task FixAllAsync( /// create multiple diagnostics for the same issue (For example, one main diagnostic and multiple 'faded out code' /// diagnostics). FixAll can be invoked from any of those, but we'll only want perform an edit for only one /// diagnostic for each of those sets of diagnostics. - /// - /// This overload differs from in that it also passes along - /// the in case that would be useful (for example if the is used. + /// This overload differs from in that it also + /// passes along the in case that would be useful (for example if the + /// CodeActionEquivalenceKey is used). /// /// Only one of these two overloads needs to be overridden if you want to customize behavior. /// @@ -117,12 +116,11 @@ protected virtual bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic, Docu /// create multiple diagnostics for the same issue (For example, one main diagnostic and multiple 'faded out code' /// diagnostics). FixAll can be invoked from any of those, but we'll only want perform an edit for only one /// diagnostic for each of those sets of diagnostics. - /// - /// By default, all diagnostics will be included in fix-all unless they are filtered out here. If only the + /// By default, all diagnostics will be included in fix-all unless they are filtered out here. If only the /// diagnostic needs to be queried to make this determination, only this overload needs to be overridden. However, - /// if information from is needed (for example ), then should be overridden instead. + /// if information from is needed (for example the CodeActionEquivalenceKey), then should be overridden + /// instead. /// /// Only one of these two overloads needs to be overridden if you want to customize behavior. /// diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs index 82fff702ad1..4a0dc3dfb2a 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/IFixAllSpanMappingService.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; /// -/// Language service for mapping spans for specific s for fix all occurences code fix. +/// Language service for mapping spans for specific s for fix all occurrences code fix. /// Every language that wants to support span based FixAll scopes, such as , /// , should implement this language service. Non-span based FixAll scopes, /// such as , and diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs index 3325f4796a1..b244cc32132 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -19,75 +20,75 @@ internal static class CodeGenerator /// public static readonly SyntaxAnnotation Annotation = new(nameof(CodeGenerator)); - private static ICodeGenerationService GetCodeGenerationService(HostWorkspaceServices services, string language) - => services.GetExtendedLanguageServices(language).GetRequiredService(); + private static ICodeGenerationService GetCodeGenerationService(Solution solution, string language) + => solution.GetExtendedLanguageServices(language).GetRequiredService(); /// /// Create a new solution where the declaration of the destination symbol has an additional event of the same signature as the specified event symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddEventDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IEventSymbol @event, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddEventAsync(context, destination, @event, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddEventAsync(context, destination, @event, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional field of the same signature as the specified field symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddFieldDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IFieldSymbol field, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddFieldAsync(context, destination, field, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddFieldAsync(context, destination, field, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional method of the same signature as the specified method symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddMethodDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IMethodSymbol method, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddMethodAsync(context, destination, method, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddMethodAsync(context, destination, method, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional property of the same signature as the specified property symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddPropertyDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IPropertySymbol property, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddPropertyAsync(context, destination, property, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddPropertyAsync(context, destination, property, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional named type of the same signature as the specified named type symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamedTypeDeclarationAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, INamedTypeSymbol namedType, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional named type of the same signature as the specified named type symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamedTypeDeclarationAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamedTypeSymbol namedType, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamedTypeAsync(context, destination, namedType, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional namespace of the same signature as the specified namespace symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamespaceDeclarationAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamespaceSymbol @namespace, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamespaceAsync(context, destination, @namespace, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamespaceAsync(context, destination, @namespace, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has an additional namespace or type of the same signature as the specified namespace or type symbol. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddNamespaceOrTypeDeclarationAsync(CodeGenerationSolutionContext context, INamespaceSymbol destination, INamespaceOrTypeSymbol namespaceOrType, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddNamespaceOrTypeAsync(context, destination, namespaceOrType, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddNamespaceOrTypeAsync(context, destination, namespaceOrType, cancellationToken); /// /// Create a new solution where the declaration of the destination symbol has additional members of the same signature as the specified member symbols. /// Returns the document in the new solution where the destination symbol is declared. /// public static Task AddMemberDeclarationsAsync(CodeGenerationSolutionContext context, INamedTypeSymbol destination, IEnumerable members, CancellationToken cancellationToken) - => GetCodeGenerationService(context.Solution.Workspace.Services, destination.Language).AddMembersAsync(context, destination, members, cancellationToken); + => GetCodeGenerationService(context.Solution, destination.Language).AddMembersAsync(context, destination, members, cancellationToken); /// /// Returns true if additional declarations can be added to the destination symbol's declaration. /// public static bool CanAdd(Solution solution, ISymbol destination, CancellationToken cancellationToken) - => GetCodeGenerationService(solution.Workspace.Services, destination.Language).CanAddTo(destination, solution, cancellationToken); + => GetCodeGenerationService(solution, destination.Language).CanAddTo(destination, solution, cancellationToken); } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs index 57c8c7b7210..db6df0f49a5 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractMethodSymbol.cs @@ -101,6 +101,8 @@ public virtual ImmutableArray ReturnTypeCustomModifiers #if !ROSLYN_4_12_OR_LOWER public bool IsIterator => false; + + public IMethodSymbol AssociatedExtensionImplementation => null; #endif public SignatureCallingConvention CallingConvention => SignatureCallingConvention.Default; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs index e695ebff662..b8dc0f9e380 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationAbstractNamedTypeSymbol.cs @@ -123,4 +123,11 @@ public override string MetadataName public bool IsSerializable => false; public bool IsFileLocal => Modifiers.IsFile; + +#if !ROSLYN_4_12_OR_LOWER + public bool IsExtension => false; + public string ExtensionGroupingName => null; + public string ExtensionMarkerName => null; + public IParameterSymbol ExtensionParameter => null; +#endif } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs index 52f4359e86f..6e102835155 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs @@ -43,9 +43,9 @@ public ImmutableArray AllInterfaces public bool IsNativeIntegerType => false; #if !ROSLYN_4_12_OR_LOWER - public bool IsExtension => false; + bool ITypeSymbol.IsExtension => false; - public IParameterSymbol ExtensionParameter => null; + IParameterSymbol ITypeSymbol.ExtensionParameter => null; #endif public static ImmutableArray TupleElementTypes => default; diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs index 322c15e27c2..63f9c0e132c 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ISolutionExtensions.cs @@ -157,4 +157,24 @@ public static Solution WithUpToDateSourceGeneratorDocuments(this Solution soluti return null; } } + + public static TLanguageService? GetLanguageService(this Solution? solution, string languageName) where TLanguageService : ILanguageService + => solution is null ? default : solution.GetExtendedLanguageServices(languageName).GetService(); + + public static TLanguageService GetRequiredLanguageService(this Solution solution, string languageName) where TLanguageService : ILanguageService + => solution.GetExtendedLanguageServices(languageName).GetRequiredService(); + +#pragma warning disable RS0030 // Do not used banned API 'Project.LanguageServices', use 'GetExtendedLanguageServices' instead - allow in this helper. + + /// + /// Gets extended host language services, which includes language services from . + /// + public static HostLanguageServices GetExtendedLanguageServices(this Solution solution, string languageName) +#if !WORKSPACE + => solution.Workspace.Services.GetExtendedLanguageServices(languageName); +#else + => solution.Services.GetExtendedLanguageServices(languageName); +#endif + +#pragma warning restore RS0030 // Do not used banned APIs } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs index 139ac3336f7..e3626a4af5f 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs @@ -15,31 +15,16 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class ProjectExtensions { public static TLanguageService? GetLanguageService(this Project? project) where TLanguageService : class, ILanguageService -#if CODE_STYLE - => project?.GetExtendedLanguageServices().GetService(); -#else - => project?.Services.GetService(); -#endif + => project?.Solution.GetLanguageService(project.Language); public static TLanguageService GetRequiredLanguageService(this Project project) where TLanguageService : class, ILanguageService -#if CODE_STYLE - => project.GetExtendedLanguageServices().GetRequiredService(); -#else - => project.Services.GetRequiredService(); -#endif + => project.Solution.GetRequiredLanguageService(project.Language); -#pragma warning disable RS0030 // Do not used banned API 'Project.LanguageServices', use 'GetExtendedLanguageServices' instead - allow in this helper. /// - /// Gets extended host language services, which includes language services from . + /// Gets extended host language services, which includes language services from . /// public static HostLanguageServices GetExtendedLanguageServices(this Project project) -#if !WORKSPACE - => project.Solution.Workspace.Services.GetExtendedLanguageServices(project.Language); -#else - => project.Solution.Services.GetExtendedLanguageServices(project.Language); -#endif - -#pragma warning restore RS0030 // Do not used banned APIs + => project.Solution.GetExtendedLanguageServices(project.Language); public static string? TryGetAnalyzerConfigPathForProjectConfiguration(this Project project) => TryGetAnalyzerConfigPathForProjectOrDiagnosticConfiguration(project, diagnostic: null); diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs index 0b41c1f9c88..9b0142c69d0 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs @@ -79,7 +79,7 @@ public static SyntaxNode Negate( return GetNegationOfBinaryExpression(expressionOrPattern, generator, generatorInternal, semanticModel, cancellationToken); if (syntaxFacts.IsLiteralExpression(expressionOrPattern)) - return GetNegationOfLiteralExpression(expressionOrPattern, generator, semanticModel); + return GetNegationOfLiteralExpression(expressionOrPattern, generator, generatorInternal, semanticModel); if (syntaxFacts.IsLogicalNotExpression(expressionOrPattern)) return GetNegationOfLogicalNotExpression(expressionOrPattern, syntaxFacts); @@ -126,9 +126,7 @@ public static SyntaxNode Negate( } if (syntaxFacts.IsRelationalPattern(expressionOrPattern)) - { return GetNegationOfRelationalPattern(expressionOrPattern, generatorInternal, patternValueType); - } return syntaxFacts.IsAnyPattern(expressionOrPattern) ? generatorInternal.NotPattern(expressionOrPattern) @@ -469,16 +467,22 @@ private static bool CanSimplifyToLengthEqualsZeroExpression( private static SyntaxNode GetNegationOfLiteralExpression( SyntaxNode expression, SyntaxGenerator generator, + SyntaxGeneratorInternal generatorInternal, SemanticModel semanticModel) { + var syntaxFacts = generatorInternal.SyntaxFacts; + var operation = semanticModel.GetOperation(expression); SyntaxNode newLiteralExpression; - if (operation?.Kind == OperationKind.Literal && operation.ConstantValue.HasValue && operation.ConstantValue.Value is true) + if (expression.RawKind == syntaxFacts.SyntaxKinds.TrueLiteralExpression || + operation is { Kind: OperationKind.Literal, ConstantValue: { HasValue: true, Value: true } }) { newLiteralExpression = generator.FalseLiteralExpression(); } - else if (operation?.Kind == OperationKind.Literal && operation.ConstantValue.HasValue && operation.ConstantValue.Value is false) + else if ( + expression.RawKind == syntaxFacts.SyntaxKinds.FalseLiteralExpression || + operation is { Kind: OperationKind.Literal, ConstantValue: { HasValue: true, Value: false } }) { newLiteralExpression = generator.TrueLiteralExpression(); } diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems index 4d20ed4ea2e..a9094aa9854 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems @@ -96,7 +96,6 @@ - diff --git a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb index 2f45d9c8a3b..9652189dc1b 100644 --- a/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb +++ b/src/roslyn/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSyntaxGeneratorInternal.vb @@ -101,6 +101,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return Parenthesize(expression, addSimplifierAnnotation) End Function + Friend Shared Function ParenthesizeNonSimple(expression As SyntaxNode, Optional addSimplifierAnnotation As Boolean = True) As ExpressionSyntax + Dim identifierName = TryCast(expression, IdentifierNameSyntax) + If identifierName IsNot Nothing Then + Return identifierName + End If + + Return Parenthesize(expression, addSimplifierAnnotation) + End Function + Friend Shared Function Parenthesize(expression As SyntaxNode, Optional addSimplifierAnnotation As Boolean = True) As ParenthesizedExpressionSyntax Return DirectCast(expression, ExpressionSyntax).Parenthesize(addSimplifierAnnotation) End Function @@ -519,7 +528,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration #End Region Public Overrides Function BitwiseOrExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.OrExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.OrExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function CastExpression(type As SyntaxNode, expression As SyntaxNode) As SyntaxNode diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index e778643388c..9f2e35caa54 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -16,7 +16,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration - Friend Class VisualBasicSyntaxGenerator + Friend NotInheritable Class VisualBasicSyntaxGenerator Inherits SyntaxGenerator Public Shared ReadOnly Instance As SyntaxGenerator = New VisualBasicSyntaxGenerator() @@ -98,12 +98,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.TupleExpression(SyntaxFactory.SeparatedList(arguments.Select(AddressOf AsSimpleArgument))) End Function - Private Shared Function Parenthesize(expression As SyntaxNode, Optional addSimplifierAnnotation As Boolean = True) As ParenthesizedExpressionSyntax - Return VisualBasicSyntaxGeneratorInternal.Parenthesize(expression, addSimplifierAnnotation) + Private Shared Function ParenthesizeNonSimple(expression As SyntaxNode, Optional addSimplifierAnnotation As Boolean = True) As ExpressionSyntax + Return VisualBasicSyntaxGeneratorInternal.ParenthesizeNonSimple(expression, addSimplifierAnnotation) End Function Public Overrides Function AddExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.AddExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.AddExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overloads Overrides Function Argument(name As String, refKind As RefKind, expression As SyntaxNode) As SyntaxNode @@ -130,7 +130,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function BitwiseAndExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.AndExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.AndExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function ConditionalExpression(condition As SyntaxNode, whenTrue As SyntaxNode, whenFalse As SyntaxNode) As SyntaxNode @@ -231,7 +231,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function IsTypeExpression(expression As SyntaxNode, type As SyntaxNode) As SyntaxNode - Return SyntaxFactory.TypeOfIsExpression(Parenthesize(expression), DirectCast(type, TypeSyntax)) + Return SyntaxFactory.TypeOfIsExpression(ParenthesizeNonSimple(expression), DirectCast(type, TypeSyntax)) End Function Public Overrides Function TypeOfExpression(type As SyntaxNode) As SyntaxNode @@ -239,15 +239,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function LogicalAndExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.AndAlsoExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.AndAlsoExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function LogicalNotExpression(expression As SyntaxNode) As SyntaxNode - Return SyntaxFactory.NotExpression(Parenthesize(expression)) + ' This helper is used in some locations where the formatter is not available. So try to ensure + ' that we generate proper code with a space between the 'Not' and the expression when necessary. + Return SyntaxFactory.NotExpression( + SyntaxFactory.Token(SyntaxKind.NotKeyword).WithTrailingTrivia(SyntaxFactory.ElasticSpace), + ParenthesizeNonSimple(expression)) End Function Public Overrides Function LogicalOrExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.OrElseExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.OrElseExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function ConditionalAccessExpression(expression As SyntaxNode, whenNotNull As SyntaxNode) As SyntaxNode @@ -264,11 +268,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function MultiplyExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.MultiplyExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.MultiplyExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function NegateExpression(expression As SyntaxNode) As SyntaxNode - Return SyntaxFactory.UnaryMinusExpression(Parenthesize(expression)) + Return SyntaxFactory.UnaryMinusExpression(ParenthesizeNonSimple(expression)) End Function Private Shared Function AsExpressionList(expressions As IEnumerable(Of SyntaxNode)) As SeparatedSyntaxList(Of ExpressionSyntax) @@ -312,11 +316,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function ReferenceEqualsExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.IsExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.IsExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function ReferenceNotEqualsExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.IsNotExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.IsNotExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function ReturnStatement(Optional expressionOpt As SyntaxNode = Nothing) As SyntaxNode @@ -402,11 +406,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function ValueEqualsExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.EqualsExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.EqualsExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function ValueNotEqualsExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.NotEqualsExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.NotEqualsExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Private Function CreateArgumentList(arguments As IEnumerable(Of SyntaxNode)) As ArgumentListSyntax @@ -545,19 +549,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function SubtractExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.SubtractExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.SubtractExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function DivideExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.DivideExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.DivideExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function ModuloExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.ModuloExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.ModuloExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function BitwiseNotExpression(operand As SyntaxNode) As SyntaxNode - Return SyntaxFactory.NotExpression(Parenthesize(operand)) + Return SyntaxFactory.NotExpression(ParenthesizeNonSimple(operand)) End Function Public Overrides Function CoalesceExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode @@ -565,19 +569,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Public Overrides Function LessThanExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.LessThanExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.LessThanExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function LessThanOrEqualExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.LessThanOrEqualExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.LessThanOrEqualExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function GreaterThanExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.GreaterThanExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.GreaterThanExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function GreaterThanOrEqualExpression(left As SyntaxNode, right As SyntaxNode) As SyntaxNode - Return SyntaxFactory.GreaterThanOrEqualExpression(Parenthesize(left), Parenthesize(right)) + Return SyntaxFactory.GreaterThanOrEqualExpression(ParenthesizeNonSimple(left), ParenthesizeNonSimple(right)) End Function Public Overrides Function TryCatchStatement(tryStatements As IEnumerable(Of SyntaxNode), catchClauses As IEnumerable(Of SyntaxNode), Optional finallyStatements As IEnumerable(Of SyntaxNode) = Nothing) As SyntaxNode @@ -3637,6 +3641,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Friend Overrides Function RemoveCommentLines(syntaxList As SyntaxTriviaList) As SyntaxTriviaList Return syntaxList.Where(Function(s) Not IsRegularOrDocComment(s)).ToSyntaxTriviaList() End Function + + Friend Overrides Function ExtensionBlockDeclaration(extensionParameter As SyntaxNode, typeParameters As IEnumerable(Of SyntaxNode), members As IEnumerable(Of SyntaxNode)) As SyntaxNode + Throw New NotSupportedException("Extension blocks are not supported in Visual Basic") + End Function + #End Region End Class diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AbstractIntrinsicOperatorDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AbstractIntrinsicOperatorDocumentation.vb index 07194defb4c..0cce47cbe77 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AbstractIntrinsicOperatorDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AbstractIntrinsicOperatorDocumentation.vb @@ -2,8 +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. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators @@ -19,20 +21,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Get End Property - Public MustOverride ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) + Public MustOverride ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) Public MustOverride Function GetParameterName(index As Integer) As String Public MustOverride Function GetParameterDocumentation(index As Integer) As String - Public Overridable Function GetParameterDisplayParts(index As Integer) As IList(Of SymbolDisplayPart) - Return {New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index))} + Public Overridable Function GetParameterDisplayParts(index As Integer) As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create(New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index))) End Function Public Overridable Function TryGetTypeNameParameter(syntaxNode As SyntaxNode, index As Integer) As TypeSyntax Return Nothing End Function - Public Overridable Function GetSuffix(semanticModel As SemanticModel, position As Integer, nodeToBind As SyntaxNode, cancellationToken As CancellationToken) As IList(Of SymbolDisplayPart) - Dim suffixParts As New List(Of SymbolDisplayPart) + Public Overridable Function GetSuffix(semanticModel As SemanticModel, position As Integer, nodeToBind As SyntaxNode, cancellationToken As CancellationToken) As ImmutableArray(Of SymbolDisplayPart) + Dim suffixParts = ArrayBuilder(Of SymbolDisplayPart).GetInstance() If IncludeAsType Then suffixParts.Add(New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, ")")) @@ -44,7 +46,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Dim typeInfo = semanticModel.GetTypeInfo(nodeToBind, cancellationToken) If typeInfo.Type IsNot Nothing Then suffixParts.AddRange(typeInfo.Type.ToMinimalDisplayParts(semanticModel, position)) - Return suffixParts + Return suffixParts.ToImmutableAndFree() End If End If @@ -58,13 +60,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators suffixParts.Add(New SymbolDisplayPart(SymbolDisplayPartKind.Text, Nothing, ReturnTypeMetadataName)) End If - Return suffixParts + Return suffixParts.ToImmutableAndFree() End If suffixParts.Add(New SymbolDisplayPart(SymbolDisplayPartKind.Text, Nothing, VBWorkspaceResources.result)) End If - Return suffixParts + Return suffixParts.ToImmutableAndFree() End Function End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AddHandlerStatementDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AddHandlerStatementDocumentation.vb index bec3592ac44..9ec15860e71 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AddHandlerStatementDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/AddHandlerStatementDocumentation.vb @@ -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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class AddHandlerStatementDocumentation Inherits AbstractAddRemoveHandlerStatementDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Associates_an_event_with_an_event_handler_delegate_or_lambda_expression_at_run_time - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Associates_an_event_with_an_event_handler_delegate_or_lambda_expression_at_run_time Public Overrides Function GetParameterDocumentation(index As Integer) As String Select Case index @@ -23,12 +22,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "AddHandler"), - New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " ")} - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "AddHandler"), + New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " ")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/BinaryConditionalExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/BinaryConditionalExpressionDocumentation.vb index afb48f4e935..89ea2262e8d 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/BinaryConditionalExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/BinaryConditionalExpressionDocumentation.vb @@ -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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class BinaryConditionalExpressionDocumentation Inherits AbstractIntrinsicOperatorDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.If_expression_evaluates_to_a_reference_or_Nullable_value_that_is_not_Nothing_the_function_returns_that_value_Otherwise_it_calculates_and_returns_expressionIfNothing - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.If_expression_evaluates_to_a_reference_or_Nullable_value_that_is_not_Nothing_the_function_returns_that_value_Otherwise_it_calculates_and_returns_expressionIfNothing Public Overrides Function GetParameterDocumentation(index As Integer) As String Select Case index @@ -34,23 +33,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return True - End Get - End Property - - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 2 - End Get - End Property - - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "If"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")} - End Get - End Property + Public Overrides ReadOnly Property IncludeAsType As Boolean = True + + Public Overrides ReadOnly Property ParameterCount As Integer = 2 + + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "If"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/CTypeCastExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/CTypeCastExpressionDocumentation.vb index 0706915921c..97a17578245 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/CTypeCastExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/CTypeCastExpressionDocumentation.vb @@ -2,23 +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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class CTypeCastExpressionDocumentation Inherits AbstractCastExpressionDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Returns_the_result_of_explicitly_converting_an_expression_to_a_specified_data_type - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Returns_the_result_of_explicitly_converting_an_expression_to_a_specified_data_type - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return { - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "CType"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(") - } - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "CType"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/DirectCastExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/DirectCastExpressionDocumentation.vb index 5233f539329..c036a33d36c 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/DirectCastExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/DirectCastExpressionDocumentation.vb @@ -2,23 +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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class DirectCastExpressionDocumentation Inherits AbstractCastExpressionDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Introduces_a_type_conversion_operation_similar_to_CType_The_difference_is_that_CType_succeeds_as_long_as_there_is_a_valid_conversion_whereas_DirectCast_requires_that_one_type_inherit_from_or_implement_the_other_type - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Introduces_a_type_conversion_operation_similar_to_CType_The_difference_is_that_CType_succeeds_as_long_as_there_is_a_valid_conversion_whereas_DirectCast_requires_that_one_type_inherit_from_or_implement_the_other_type - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return { - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "DirectCast"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(") - } - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "DirectCast"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetTypeExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetTypeExpressionDocumentation.vb index 333c15ae538..41202eac35a 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetTypeExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetTypeExpressionDocumentation.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 Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators @@ -26,32 +27,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 1 - End Get - End Property + Public Overrides ReadOnly Property ParameterCount As Integer = 1 - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Returns_a_System_Type_object_for_the_specified_type_name - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Returns_a_System_Type_object_for_the_specified_type_name - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return { - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "GetType"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(") - } - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "GetType"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return True - End Get - End Property + Public Overrides ReadOnly Property IncludeAsType As Boolean = True Public Overrides Function TryGetTypeNameParameter(syntaxNode As SyntaxNode, index As Integer) As TypeSyntax Dim getTypeExpression = TryCast(syntaxNode, GetTypeExpressionSyntax) @@ -63,10 +48,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End If End Function - Public Overrides ReadOnly Property ReturnTypeMetadataName As String - Get - Return "System.Type" - End Get - End Property + Public Overrides ReadOnly Property ReturnTypeMetadataName As String = "System.Type" End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetXmlNamespaceExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetXmlNamespaceExpressionDocumentation.vb index bca09bb0edf..f4637306c4b 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetXmlNamespaceExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/GetXmlNamespaceExpressionDocumentation.vb @@ -2,18 +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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class GetXmlNamespaceExpressionDocumentation Inherits AbstractIntrinsicOperatorDocumentation - Public Overrides Function GetParameterDisplayParts(index As Integer) As IList(Of SymbolDisplayPart) + Public Overrides Function GetParameterDisplayParts(index As Integer) As ImmutableArray(Of SymbolDisplayPart) Select Case index Case 0 - Return { + Return ImmutableArray.Create( New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "["), New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index)), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "]") - } + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "]")) Case Else Throw New ArgumentException(NameOf(index)) End Select @@ -37,37 +38,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 1 - End Get - End Property + Public Overrides ReadOnly Property ParameterCount As Integer = 1 - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Returns_the_System_Xml_Linq_XNamespace_object_corresponding_to_the_specified_XML_namespace_prefix - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Returns_the_System_Xml_Linq_XNamespace_object_corresponding_to_the_specified_XML_namespace_prefix - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return { - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "GetXmlNamespace"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(") - } - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "GetXmlNamespace"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return True - End Get - End Property + Public Overrides ReadOnly Property IncludeAsType As Boolean = True - Public Overrides ReadOnly Property ReturnTypeMetadataName As String - Get - Return "System.Xml.Linq.XNamespace" - End Get - End Property + Public Overrides ReadOnly Property ReturnTypeMetadataName As String = "System.Xml.Linq.XNamespace" End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/MidAssignmentDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/MidAssignmentDocumentation.vb index d3323a51a22..48d1960c0c2 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/MidAssignmentDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/MidAssignmentDocumentation.vb @@ -2,17 +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. +Imports System.Collections.Immutable Imports System.Threading Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class MidAssignmentDocumentation Inherits AbstractIntrinsicOperatorDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Replaces_a_specified_number_of_characters_in_a_String_variable_with_characters_from_another_string - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Replaces_a_specified_number_of_characters_in_a_String_variable_with_characters_from_another_string Public Overrides Function GetParameterDocumentation(index As Integer) As String Select Case index @@ -40,41 +38,29 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides Function GetParameterDisplayParts(index As Integer) As IList(Of SymbolDisplayPart) + Public Overrides Function GetParameterDisplayParts(index As Integer) As ImmutableArray(Of SymbolDisplayPart) If index = 2 Then - Return {New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, "[" + GetParameterName(2) + "]")} + Return ImmutableArray.Create(New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, "[" + GetParameterName(2) + "]")) Else Return MyBase.GetParameterDisplayParts(index) End If End Function - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return False - End Get - End Property + Public Overrides ReadOnly Property IncludeAsType As Boolean = False - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 3 - End Get - End Property + Public Overrides ReadOnly Property ParameterCount As Integer = 3 - Public Overrides Function GetSuffix(semanticModel As SemanticModel, position As Integer, nodeToBind As SyntaxNode, cancellationToken As CancellationToken) As IList(Of SymbolDisplayPart) - Return { + Public Overrides Function GetSuffix(semanticModel As SemanticModel, position As Integer, nodeToBind As SyntaxNode, cancellationToken As CancellationToken) As ImmutableArray(Of SymbolDisplayPart) + Return ImmutableArray.Create( New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, ")"), New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " "), New SymbolDisplayPart(SymbolDisplayPartKind.Operator, Nothing, "="), New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " "), - New SymbolDisplayPart(SymbolDisplayPartKind.Text, Nothing, VBWorkspaceResources.stringExpression) - } + New SymbolDisplayPart(SymbolDisplayPartKind.Text, Nothing, VBWorkspaceResources.stringExpression)) End Function - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "Mid"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")} - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "Mid"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/NameOfExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/NameOfExpressionDocumentation.vb index 30f466aa2ae..40c585805af 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/NameOfExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/NameOfExpressionDocumentation.vb @@ -2,34 +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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class NameOfExpressionDocumentation Inherits AbstractIntrinsicOperatorDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Produces_a_string_for_the_name_of_the_specified_type_or_member - End Get - End Property - - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return True - End Get - End Property - - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 1 - End Get - End Property - - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "NameOf"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")} - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Produces_a_string_for_the_name_of_the_specified_type_or_member + + Public Overrides ReadOnly Property IncludeAsType As Boolean = True + + Public Overrides ReadOnly Property ParameterCount As Integer = 1 + + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "NameOf"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) Public Overrides Function GetParameterDocumentation(index As Integer) As String Select Case index @@ -49,11 +37,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property ReturnTypeMetadataName As String - Get - Return "System.String" - End Get - End Property - + Public Overrides ReadOnly Property ReturnTypeMetadataName As String = "System.String" End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/PredefinedCastExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/PredefinedCastExpressionDocumentation.vb index c558b64b8b5..52189120ce2 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/PredefinedCastExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/PredefinedCastExpressionDocumentation.vb @@ -2,6 +2,8 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class PredefinedCastExpressionDocumentation Inherits AbstractIntrinsicOperatorDocumentation @@ -38,22 +40,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return True - End Get - End Property + Public Overrides ReadOnly Property IncludeAsType As Boolean = True - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 1 - End Get - End Property + Public Overrides ReadOnly Property ParameterCount As Integer = 1 - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, _keywordText), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")} + Return ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, _keywordText), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Get End Property diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/RemoveHandlerStatementDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/RemoveHandlerStatementDocumentation.vb index 17f497af4b0..57938fc18d0 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/RemoveHandlerStatementDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/RemoveHandlerStatementDocumentation.vb @@ -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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class RemoveHandlerStatementDocumentation Inherits AbstractAddRemoveHandlerStatementDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Removes_the_association_between_an_event_and_an_event_handler_or_delegate_at_run_time - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Removes_the_association_between_an_event_and_an_event_handler_or_delegate_at_run_time Public Overrides Function GetParameterDocumentation(index As Integer) As String Select Case index @@ -23,11 +22,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "RemoveHandler"), - New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " ")} - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "RemoveHandler"), + New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " ")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TernaryConditionalExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TernaryConditionalExpressionDocumentation.vb index bdcee3f9a58..307e7c1dd5e 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TernaryConditionalExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TernaryConditionalExpressionDocumentation.vb @@ -2,25 +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 System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class TernaryConditionalExpressionDocumentation Inherits AbstractIntrinsicOperatorDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.If_condition_returns_True_the_function_calculates_and_returns_expressionIfTrue_Otherwise_it_returns_expressionIfFalse - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.If_condition_returns_True_the_function_calculates_and_returns_expressionIfTrue_Otherwise_it_returns_expressionIfFalse - Public Overrides Function GetParameterDisplayParts(index As Integer) As IList(Of SymbolDisplayPart) + Public Overrides Function GetParameterDisplayParts(index As Integer) As ImmutableArray(Of SymbolDisplayPart) If index = 0 Then - Return {New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index)), - New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " "), - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "As"), - New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " "), - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "Boolean")} + Return ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index)), + New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " "), + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "As"), + New SymbolDisplayPart(SymbolDisplayPartKind.Space, Nothing, " "), + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "Boolean")) Else - Return {New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index))} + Return ImmutableArray.Create(New SymbolDisplayPart(SymbolDisplayPartKind.ParameterName, Nothing, GetParameterName(index))) End If End Function @@ -50,23 +50,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators End Select End Function - Public Overrides ReadOnly Property IncludeAsType As Boolean - Get - Return True - End Get - End Property + Public Overrides ReadOnly Property IncludeAsType As Boolean = True - Public Overrides ReadOnly Property ParameterCount As Integer - Get - Return 3 - End Get - End Property + Public Overrides ReadOnly Property ParameterCount As Integer = 3 - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return {New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "If"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")} - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "If"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TryCastExpressionDocumentation.vb b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TryCastExpressionDocumentation.vb index 1465a18832b..154a906004f 100644 --- a/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TryCastExpressionDocumentation.vb +++ b/src/roslyn/src/Workspaces/VisualBasic/Portable/Utilities/IntrinsicOperators/TryCastExpressionDocumentation.vb @@ -2,23 +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. +Imports System.Collections.Immutable + Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities.IntrinsicOperators Friend NotInheritable Class TryCastExpressionDocumentation Inherits AbstractCastExpressionDocumentation - Public Overrides ReadOnly Property DocumentationText As String - Get - Return VBWorkspaceResources.Introduces_a_type_conversion_operation_that_does_not_throw_an_exception_If_an_attempted_conversion_fails_TryCast_returns_Nothing_which_your_program_can_test_for - End Get - End Property + Public Overrides ReadOnly Property DocumentationText As String = + VBWorkspaceResources.Introduces_a_type_conversion_operation_that_does_not_throw_an_exception_If_an_attempted_conversion_fails_TryCast_returns_Nothing_which_your_program_can_test_for - Public Overrides ReadOnly Property PrefixParts As IList(Of SymbolDisplayPart) - Get - Return { - New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "TryCast"), - New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(") - } - End Get - End Property + Public Overrides ReadOnly Property PrefixParts As ImmutableArray(Of SymbolDisplayPart) = ImmutableArray.Create( + New SymbolDisplayPart(SymbolDisplayPartKind.Keyword, Nothing, "TryCast"), + New SymbolDisplayPart(SymbolDisplayPartKind.Punctuation, Nothing, "(")) End Class End Namespace diff --git a/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb b/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb index 396e593be60..7f71cdbb7b7 100644 --- a/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb +++ b/src/roslyn/src/Workspaces/VisualBasicTest/CodeGeneration/SyntaxGeneratorTests.vb @@ -4,6 +4,7 @@ Imports System.Globalization Imports System.Runtime.InteropServices +Imports Microsoft.CodeAnalysis.CSharp Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.Test.Utilities @@ -296,35 +297,35 @@ End Class Public Sub TestMathAndLogicExpressions() - VerifySyntax(Of UnaryExpressionSyntax)(Generator.NegateExpression(Generator.IdentifierName("x")), "-(x)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) + (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.SubtractExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) - (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.MultiplyExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) * (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.DivideExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) / (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.ModuloExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) Mod (y)") + VerifySyntax(Of UnaryExpressionSyntax)(Generator.NegateExpression(Generator.IdentifierName("x")), "-x") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x + y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.SubtractExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x - y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.MultiplyExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x * y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.DivideExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x / y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.ModuloExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x Mod y") - VerifySyntax(Of UnaryExpressionSyntax)(Generator.BitwiseNotExpression(Generator.IdentifierName("x")), "Not(x)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.BitwiseAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) And (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.BitwiseOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) Or (y)") + VerifySyntax(Of UnaryExpressionSyntax)(Generator.BitwiseNotExpression(Generator.IdentifierName("x")), "Not x") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.BitwiseAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x And y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.BitwiseOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x Or y") - VerifySyntax(Of UnaryExpressionSyntax)(Generator.LogicalNotExpression(Generator.IdentifierName("x")), "Not(x)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.LogicalAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) AndAlso (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.LogicalOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) OrElse (y)") + VerifySyntax(Of UnaryExpressionSyntax)(Generator.LogicalNotExpression(Generator.IdentifierName("x")), "Not x") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.LogicalAndExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x AndAlso y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.LogicalOrExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x OrElse y") End Sub Public Sub TestEqualityAndInequalityExpressions() - VerifySyntax(Of BinaryExpressionSyntax)(Generator.ReferenceEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) Is (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.ValueEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) = (y)") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.ReferenceEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x Is y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.ValueEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x = y") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.ReferenceNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) IsNot (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.ValueNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) <> (y)") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.ReferenceNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x IsNot y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.ValueNotEqualsExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x <> y") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.LessThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) < (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.LessThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) <= (y)") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.LessThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x < y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.LessThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x <= y") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.GreaterThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) > (y)") - VerifySyntax(Of BinaryExpressionSyntax)(Generator.GreaterThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "(x) >= (y)") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.GreaterThanExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x > y") + VerifySyntax(Of BinaryExpressionSyntax)(Generator.GreaterThanOrEqualExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "x >= y") End Sub @@ -340,8 +341,8 @@ End Class VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.MemberAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "x.y.z") VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.InvocationExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "x(y).z") VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.ElementAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "x(y).z") - VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "((x) + (y)).z") - VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.NegateExpression(Generator.IdentifierName("x")), Generator.IdentifierName("y")), "(-(x)).y") + VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), "(x + y).z") + VerifySyntax(Of MemberAccessExpressionSyntax)(Generator.MemberAccessExpression(Generator.NegateExpression(Generator.IdentifierName("x")), Generator.IdentifierName("y")), "(-x).y") End Sub @@ -398,7 +399,7 @@ End Class VerifySyntax(Of InvocationExpressionSyntax)( Generator.ElementAccessExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), Generator.IdentifierName("z")), - "((x) + (y))(z)") + "(x + y)(z)") End Sub @@ -409,7 +410,7 @@ End Class Public Sub TestIsAndAsExpressions() - VerifySyntax(Of TypeOfExpressionSyntax)(Generator.IsTypeExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "TypeOf(x) Is y") + VerifySyntax(Of TypeOfExpressionSyntax)(Generator.IsTypeExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "TypeOf x Is y") VerifySyntax(Of TryCastExpressionSyntax)(Generator.TryCastExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y")), "TryCast(x, y)") VerifySyntax(Of GetTypeExpressionSyntax)(Generator.TypeOfExpression(Generator.IdentifierName("x")), "GetType(x)") End Sub @@ -429,7 +430,7 @@ End Class VerifySyntax(Of InvocationExpressionSyntax)(Generator.InvocationExpression(Generator.MemberAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "x.y()") VerifySyntax(Of InvocationExpressionSyntax)(Generator.InvocationExpression(Generator.ElementAccessExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "x(y)()") VerifySyntax(Of InvocationExpressionSyntax)(Generator.InvocationExpression(Generator.InvocationExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "x(y)()") - VerifySyntax(Of InvocationExpressionSyntax)(Generator.InvocationExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "((x) + (y))()") + VerifySyntax(Of InvocationExpressionSyntax)(Generator.InvocationExpression(Generator.AddExpression(Generator.IdentifierName("x"), Generator.IdentifierName("y"))), "(x + y)()") End Sub @@ -2475,6 +2476,22 @@ End Class")) "Public Const MaxValue As System.UInt32 = 4294967295UI") End Sub + + Public Sub TestExtensionBlock_01() + Dim code = " +static class E +{ + extension(int) + { + public void M() { } + } +} +" + Dim compilation = CSharpCompilation.Create("test").AddReferences(NetFramework.mscorlib).AddSyntaxTrees(CSharp.SyntaxFactory.ParseSyntaxTree(code)) + Dim e = compilation.GlobalNamespace.GetTypeMembers("E").Single() + Assert.Throws(Of NotSupportedException)(Sub() Generator.Declaration(e)) + End Sub + #End Region #Region "Add/Insert/Remove/Get/Set members & elements" diff --git a/src/source-manifest.json b/src/source-manifest.json index 95c227ec04c..c533a381b4a 100644 --- a/src/source-manifest.json +++ b/src/source-manifest.json @@ -1,221 +1,221 @@ -{ - "repositories": [ - { - "barId": 281655, - "path": "arcade", - "remoteUri": "https://github.com/dotnet/arcade", - "commitSha": "19b800616b55f3fae5098eb0dad0a63a15747a00" - }, - { - "barId": 281307, - "path": "aspnetcore", - "remoteUri": "https://github.com/dotnet/aspnetcore", - "commitSha": "0e3d342304003f94795885e99b9bf6a6f0e36272" - }, - { - "barId": 277712, - "path": "cecil", - "remoteUri": "https://github.com/dotnet/cecil", - "commitSha": "2a768f2c8a54077cd7d788bb135caeb4d7206a15" - }, - { - "barId": 278546, - "path": "command-line-api", - "remoteUri": "https://github.com/dotnet/command-line-api", - "commitSha": "0b4618bc860374941e605d8eb1d2bc29c32801db" - }, - { - "barId": 281094, - "path": "deployment-tools", - "remoteUri": "https://github.com/dotnet/deployment-tools", - "commitSha": "c3b8848af005deb9cab2a64b47a39f507f095835" - }, - { - "barId": 279003, - "path": "diagnostics", - "remoteUri": "https://github.com/dotnet/diagnostics", - "commitSha": "a321e262b120841f4bd7651da46826589404ac66" - }, - { - "barId": 281538, - "path": "efcore", - "remoteUri": "https://github.com/dotnet/efcore", - "commitSha": "63dba7aeb6fd95a4172daed2d4b007b9491d6cd5" - }, - { - "barId": 280647, - "path": "emsdk", - "remoteUri": "https://github.com/dotnet/emsdk", - "commitSha": "8414d1e1025bbfaca0829a595d2654836fd0034b" - }, - { - "barId": 281837, - "path": "fsharp", - "remoteUri": "https://github.com/dotnet/fsharp", - "commitSha": "226338e06e4773daa8e08f2d80b4af4261868742" - }, - { - "barId": 281532, - "path": "msbuild", - "remoteUri": "https://github.com/dotnet/msbuild", - "commitSha": "0ae4996b3e0c82868b3021975d6a28f5512b0bca" - }, - { - "barId": 279847, - "path": "nuget-client", - "remoteUri": "https://github.com/nuget/nuget.client", - "commitSha": "56f4657d7585ec12b61c623756ca4b2b810c4863" - }, - { - "barId": 281744, - "path": "razor", - "remoteUri": "https://github.com/dotnet/razor", - "commitSha": "ce45cf4a51dafc16d1b478612c46792f43cad7e6" - }, - { - "barId": 281892, - "path": "roslyn", - "remoteUri": "https://github.com/dotnet/roslyn", - "commitSha": "a70b5c16d07b7cba6c4955034a0c5957c86eb6e5" - }, - { - "barId": 281884, - "path": "runtime", - "remoteUri": "https://github.com/dotnet/runtime", - "commitSha": "6a8720f87a240fba6e5e048a6a9943a1075ba717" - }, - { - "barId": 277711, - "path": "scenario-tests", - "remoteUri": "https://github.com/dotnet/scenario-tests", - "commitSha": "082359066ee0064039b9b1f1f025bdd0507d06de" - }, - { - "barId": 279874, - "path": "sdk", - "remoteUri": "https://github.com/dotnet/sdk", - "commitSha": "b359077ae4b76b6c2455cc5cecd66bb8e7a8e290" - }, - { - "barId": 281227, - "path": "source-build-reference-packages", - "remoteUri": "https://github.com/dotnet/source-build-reference-packages", - "commitSha": "25c18c250f2802a93bf97a269ce48ab518340e06" - }, - { - "barId": 277912, - "path": "sourcelink", - "remoteUri": "https://github.com/dotnet/sourcelink", - "commitSha": "9b949eeb2d5dba635c06ae936b50d2141b0aabe2" - }, - { - "barId": 277806, - "path": "symreader", - "remoteUri": "https://github.com/dotnet/symreader", - "commitSha": "9994998c0b0fab5efd5cbe2e13a3ea74f4e8e6e1" - }, - { - "barId": 278461, - "path": "templating", - "remoteUri": "https://github.com/dotnet/templating", - "commitSha": "b67630f060f02a6f5ef7cfcea731dce0d13e1ad6" - }, - { - "barId": 280001, - "path": "vstest", - "remoteUri": "https://github.com/microsoft/vstest", - "commitSha": "9cbffb03835ba1e99643da46f2fc2678a0c1b5f7" - }, - { - "barId": 278644, - "path": "windowsdesktop", - "remoteUri": "https://github.com/dotnet/windowsdesktop", - "commitSha": "5cf429afe4ec59b46e8b792bb1aac1bdd5f2d235" - }, - { - "barId": 281889, - "path": "winforms", - "remoteUri": "https://github.com/dotnet/winforms", - "commitSha": "e84c0eccb855f3a73d92b5e0422063d040068a04" - }, - { - "barId": 279242, - "path": "wpf", - "remoteUri": "https://github.com/dotnet/wpf", - "commitSha": "e103fb80eb329adb54a804ba88f12e65cabd8744" - }, - { - "barId": 278018, - "path": "xdt", - "remoteUri": "https://github.com/dotnet/xdt", - "commitSha": "ec086785dfa9af4a0dc58eca3e5c969e3d0c6003" - } - ], - "submodules": [ - { - "path": "aspnetcore/src/submodules/googletest", - "remoteUri": "https://github.com/google/googletest", - "commitSha": "6986c2b575f77135401a4e1c65a7a42f20e18fef" - }, - { - "path": "aspnetcore/src/submodules/MessagePack-CSharp", - "remoteUri": "https://github.com/aspnet/MessagePack-CSharp.git", - "commitSha": "9aeb12b9bdb024512ffe2e4bddfa2785dca6e39e" - }, - { - "path": "nuget-client/submodules/NuGet.Build.Localization", - "remoteUri": "https://github.com/NuGet/NuGet.Build.Localization.git", - "commitSha": "f15db7b7c6f5affbea268632ef8333d2687c8031" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/abstractions-xunit", - "remoteUri": "https://github.com/xunit/abstractions.xunit", - "commitSha": "b75d54d73b141709f805c2001b16f3dd4d71539d" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/application-insights", - "remoteUri": "https://github.com/microsoft/ApplicationInsights-dotnet", - "commitSha": "2faa7e8b157a431daa2e71785d68abd5fa817b53" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet", - "remoteUri": "https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet.git", - "commitSha": "e67b25be77532af9ba405670b34b4d263d505fde" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/cssparser", - "remoteUri": "https://github.com/dotnet/cssparser", - "commitSha": "0d59611784841735a7778a67aa6e9d8d000c861f" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/docker-creds-provider", - "remoteUri": "https://github.com/mthalman/docker-creds-provider", - "commitSha": "6e1ecd0a80755f9f0e88dc23b98b52f51a77c65e" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/humanizer", - "remoteUri": "https://github.com/Humanizr/Humanizer", - "commitSha": "3ebc38de585fc641a04b0e78ed69468453b0f8a1" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/MSBuildLocator", - "remoteUri": "https://github.com/microsoft/MSBuildLocator", - "commitSha": "6235ee4484ceb8f9db32826ae252b0a2aad0955e" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/newtonsoft-json", - "remoteUri": "https://github.com/JamesNK/Newtonsoft.Json.git", - "commitSha": "0a2e291c0d9c0c7675d445703e51750363a549ef" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/spectre-console", - "remoteUri": "https://github.com/spectreconsole/spectre.console", - "commitSha": "68fcfe0de4c0602b1c4d4c3cf58ea70e9f06388c" - }, - { - "path": "source-build-reference-packages/src/externalPackages/src/vs-solutionpersistence", - "remoteUri": "https://github.com/microsoft/vs-solutionpersistence.git", - "commitSha": "0b6f82a4073ce0ff0419991ea0cd6dd6898a51ac" - } - ] +{ + "repositories": [ + { + "barId": 281655, + "path": "arcade", + "remoteUri": "https://github.com/dotnet/arcade", + "commitSha": "19b800616b55f3fae5098eb0dad0a63a15747a00" + }, + { + "barId": 281307, + "path": "aspnetcore", + "remoteUri": "https://github.com/dotnet/aspnetcore", + "commitSha": "0e3d342304003f94795885e99b9bf6a6f0e36272" + }, + { + "barId": 277712, + "path": "cecil", + "remoteUri": "https://github.com/dotnet/cecil", + "commitSha": "2a768f2c8a54077cd7d788bb135caeb4d7206a15" + }, + { + "barId": 278546, + "path": "command-line-api", + "remoteUri": "https://github.com/dotnet/command-line-api", + "commitSha": "0b4618bc860374941e605d8eb1d2bc29c32801db" + }, + { + "barId": 281094, + "path": "deployment-tools", + "remoteUri": "https://github.com/dotnet/deployment-tools", + "commitSha": "c3b8848af005deb9cab2a64b47a39f507f095835" + }, + { + "barId": 279003, + "path": "diagnostics", + "remoteUri": "https://github.com/dotnet/diagnostics", + "commitSha": "a321e262b120841f4bd7651da46826589404ac66" + }, + { + "barId": 281538, + "path": "efcore", + "remoteUri": "https://github.com/dotnet/efcore", + "commitSha": "63dba7aeb6fd95a4172daed2d4b007b9491d6cd5" + }, + { + "barId": 280647, + "path": "emsdk", + "remoteUri": "https://github.com/dotnet/emsdk", + "commitSha": "8414d1e1025bbfaca0829a595d2654836fd0034b" + }, + { + "barId": 281837, + "path": "fsharp", + "remoteUri": "https://github.com/dotnet/fsharp", + "commitSha": "226338e06e4773daa8e08f2d80b4af4261868742" + }, + { + "barId": 281532, + "path": "msbuild", + "remoteUri": "https://github.com/dotnet/msbuild", + "commitSha": "0ae4996b3e0c82868b3021975d6a28f5512b0bca" + }, + { + "barId": 279847, + "path": "nuget-client", + "remoteUri": "https://github.com/nuget/nuget.client", + "commitSha": "56f4657d7585ec12b61c623756ca4b2b810c4863" + }, + { + "barId": 281744, + "path": "razor", + "remoteUri": "https://github.com/dotnet/razor", + "commitSha": "ce45cf4a51dafc16d1b478612c46792f43cad7e6" + }, + { + "barId": 438, + "path": "roslyn", + "remoteUri": "https://github.com/maestro-auth-test/roslyn", + "commitSha": "68435db2d073b4ae1110800ab2d35cde7a59b761" + }, + { + "barId": 281884, + "path": "runtime", + "remoteUri": "https://github.com/dotnet/runtime", + "commitSha": "6a8720f87a240fba6e5e048a6a9943a1075ba717" + }, + { + "barId": 277711, + "path": "scenario-tests", + "remoteUri": "https://github.com/dotnet/scenario-tests", + "commitSha": "082359066ee0064039b9b1f1f025bdd0507d06de" + }, + { + "barId": 279874, + "path": "sdk", + "remoteUri": "https://github.com/dotnet/sdk", + "commitSha": "b359077ae4b76b6c2455cc5cecd66bb8e7a8e290" + }, + { + "barId": 281227, + "path": "source-build-reference-packages", + "remoteUri": "https://github.com/dotnet/source-build-reference-packages", + "commitSha": "25c18c250f2802a93bf97a269ce48ab518340e06" + }, + { + "barId": 277912, + "path": "sourcelink", + "remoteUri": "https://github.com/dotnet/sourcelink", + "commitSha": "9b949eeb2d5dba635c06ae936b50d2141b0aabe2" + }, + { + "barId": 277806, + "path": "symreader", + "remoteUri": "https://github.com/dotnet/symreader", + "commitSha": "9994998c0b0fab5efd5cbe2e13a3ea74f4e8e6e1" + }, + { + "barId": 278461, + "path": "templating", + "remoteUri": "https://github.com/dotnet/templating", + "commitSha": "b67630f060f02a6f5ef7cfcea731dce0d13e1ad6" + }, + { + "barId": 280001, + "path": "vstest", + "remoteUri": "https://github.com/microsoft/vstest", + "commitSha": "9cbffb03835ba1e99643da46f2fc2678a0c1b5f7" + }, + { + "barId": 278644, + "path": "windowsdesktop", + "remoteUri": "https://github.com/dotnet/windowsdesktop", + "commitSha": "5cf429afe4ec59b46e8b792bb1aac1bdd5f2d235" + }, + { + "barId": 281889, + "path": "winforms", + "remoteUri": "https://github.com/dotnet/winforms", + "commitSha": "e84c0eccb855f3a73d92b5e0422063d040068a04" + }, + { + "barId": 279242, + "path": "wpf", + "remoteUri": "https://github.com/dotnet/wpf", + "commitSha": "e103fb80eb329adb54a804ba88f12e65cabd8744" + }, + { + "barId": 278018, + "path": "xdt", + "remoteUri": "https://github.com/dotnet/xdt", + "commitSha": "ec086785dfa9af4a0dc58eca3e5c969e3d0c6003" + } + ], + "submodules": [ + { + "path": "aspnetcore/src/submodules/googletest", + "remoteUri": "https://github.com/google/googletest", + "commitSha": "6986c2b575f77135401a4e1c65a7a42f20e18fef" + }, + { + "path": "aspnetcore/src/submodules/MessagePack-CSharp", + "remoteUri": "https://github.com/aspnet/MessagePack-CSharp.git", + "commitSha": "9aeb12b9bdb024512ffe2e4bddfa2785dca6e39e" + }, + { + "path": "nuget-client/submodules/NuGet.Build.Localization", + "remoteUri": "https://github.com/NuGet/NuGet.Build.Localization.git", + "commitSha": "f15db7b7c6f5affbea268632ef8333d2687c8031" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/abstractions-xunit", + "remoteUri": "https://github.com/xunit/abstractions.xunit", + "commitSha": "b75d54d73b141709f805c2001b16f3dd4d71539d" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/application-insights", + "remoteUri": "https://github.com/microsoft/ApplicationInsights-dotnet", + "commitSha": "2faa7e8b157a431daa2e71785d68abd5fa817b53" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/azure-activedirectory-identitymodel-extensions-for-dotnet", + "remoteUri": "https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet.git", + "commitSha": "e67b25be77532af9ba405670b34b4d263d505fde" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/cssparser", + "remoteUri": "https://github.com/dotnet/cssparser", + "commitSha": "0d59611784841735a7778a67aa6e9d8d000c861f" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/docker-creds-provider", + "remoteUri": "https://github.com/mthalman/docker-creds-provider", + "commitSha": "6e1ecd0a80755f9f0e88dc23b98b52f51a77c65e" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/humanizer", + "remoteUri": "https://github.com/Humanizr/Humanizer", + "commitSha": "3ebc38de585fc641a04b0e78ed69468453b0f8a1" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/MSBuildLocator", + "remoteUri": "https://github.com/microsoft/MSBuildLocator", + "commitSha": "6235ee4484ceb8f9db32826ae252b0a2aad0955e" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/newtonsoft-json", + "remoteUri": "https://github.com/JamesNK/Newtonsoft.Json.git", + "commitSha": "0a2e291c0d9c0c7675d445703e51750363a549ef" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/spectre-console", + "remoteUri": "https://github.com/spectreconsole/spectre.console", + "commitSha": "68fcfe0de4c0602b1c4d4c3cf58ea70e9f06388c" + }, + { + "path": "source-build-reference-packages/src/externalPackages/src/vs-solutionpersistence", + "remoteUri": "https://github.com/microsoft/vs-solutionpersistence.git", + "commitSha": "0b6f82a4073ce0ff0419991ea0cd6dd6898a51ac" + } + ] } \ No newline at end of file