diff --git a/.github/ISSUE_TEMPLATE/z-apidocs-feedback.yml b/.github/ISSUE_TEMPLATE/z-apidocs-feedback.yml new file mode 100644 index 0000000000000..4ebe78e3dfa34 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/z-apidocs-feedback.yml @@ -0,0 +1,46 @@ +name: Learn feedback control. +description: | + ⛔ This template is hooked into the feedback control on Roslyn API documentation on docs.microsoft.com. It automatically fills in several fields for you. Don't use for other purposes. ⛔ +body: + - type: markdown + attributes: + value: "## Issue information" + - type: markdown + attributes: + value: Select the issue type, and describe the issue in the text box below. Add as much detail as needed to help us resolve the issue. + - type: dropdown + id: issue-type + attributes: + label: Type of issue + options: + - Typo + - Code doesn't work + - Missing information + - Outdated article + - Other (describe below) + validations: + required: true + - type: textarea + id: feedback + validations: + required: true + attributes: + label: Description + - type: markdown + attributes: + value: "## 🚧 Article information 🚧" + - type: markdown + attributes: + value: "*Don't modify the following fields*. They are automatically filled in for you. Doing so will disconnect your issue from the affected article. *Don't edit them*." + - type: input + id: pageUrl + validations: + required: true + attributes: + label: Page URL + - type: input + id: contentSourceUrl + validations: + required: true + attributes: + label: Content source URL diff --git a/Roslyn.sln b/Roslyn.sln index b682dc262a06b..43bbc84d09a61 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31319.15 @@ -363,8 +362,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Lang EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Debugger", "src\Tools\ExternalAccess\Debugger\Microsoft.CodeAnalysis.ExternalAccess.Debugger.csproj", "{655A5B07-39B8-48CD-8590-8AC0C2B708D8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config", "src\Setup\DevDivVsix\ServiceHubConfig\Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config.csproj", "{3D33BBFD-EC63-4E8C-A714-0A48A3809A87}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests", "src\Tools\ExternalAccess\FSharpTest\Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests.csproj", "{BFFB5CAE-33B5-447E-9218-BDEBFDA96CB5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Apex", "src\Tools\ExternalAccess\Apex\Microsoft.CodeAnalysis.ExternalAccess.Apex.csproj", "{FC32EF16-31B1-47B3-B625-A80933CB3F29}" @@ -544,6 +541,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Feat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Features.Test.Utilities", "src\Features\TestUtilities\Microsoft.CodeAnalysis.Features.Test.Utilities.csproj", "{5762E483-75CE-4328-A410-511F30737712}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics", "src\Tools\ExternalAccess\VisualDiagnostics\Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.csproj", "{6D819E80-BA2F-4317-8368-37F8F4434D3A}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F3118AE-8D36-4384-8E80-BD6566365305}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{47D004BE-F797-430E-8A18-4B0CDFD56643}" @@ -556,6 +555,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommonLanguageSer EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests", "src\Compilers\CSharp\Test\Emit3\Microsoft.CodeAnalysis.CSharp.Emit3.UnitTests.csproj", "{4E273CBC-BB1D-4AC1-91DB-C62FC83E0350}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SemanticSearch", "SemanticSearch", "{52ABB0E4-C3A1-4897-B51B-18EDA83F5D20}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SemanticSearch.BuildTask", "src\Tools\SemanticSearch\BuildTask\SemanticSearch.BuildTask.csproj", "{FCE88BBD-9BBD-4871-B9B0-DE176D73A6B0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SemanticSearch.ReferenceAssemblies", "src\Tools\SemanticSearch\ReferenceAssemblies\SemanticSearch.ReferenceAssemblies.csproj", "{EDEF898A-CEFA-4151-8168-D0231A602093}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SemanticSearch.BuildTask.UnitTests", "src\Tools\SemanticSearch\Tests\SemanticSearch.BuildTask.UnitTests.csproj", "{D817E3CE-F603-499B-B02A-7DECD017B170}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1106,10 +1113,6 @@ Global {655A5B07-39B8-48CD-8590-8AC0C2B708D8}.Debug|Any CPU.Build.0 = Debug|Any CPU {655A5B07-39B8-48CD-8590-8AC0C2B708D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {655A5B07-39B8-48CD-8590-8AC0C2B708D8}.Release|Any CPU.Build.0 = Release|Any CPU - {3D33BBFD-EC63-4E8C-A714-0A48A3809A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3D33BBFD-EC63-4E8C-A714-0A48A3809A87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3D33BBFD-EC63-4E8C-A714-0A48A3809A87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3D33BBFD-EC63-4E8C-A714-0A48A3809A87}.Release|Any CPU.Build.0 = Release|Any CPU {BFFB5CAE-33B5-447E-9218-BDEBFDA96CB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BFFB5CAE-33B5-447E-9218-BDEBFDA96CB5}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFFB5CAE-33B5-447E-9218-BDEBFDA96CB5}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1358,6 +1361,10 @@ Global {5762E483-75CE-4328-A410-511F30737712}.Debug|Any CPU.Build.0 = Debug|Any CPU {5762E483-75CE-4328-A410-511F30737712}.Release|Any CPU.ActiveCfg = Release|Any CPU {5762E483-75CE-4328-A410-511F30737712}.Release|Any CPU.Build.0 = Release|Any CPU + {6D819E80-BA2F-4317-8368-37F8F4434D3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D819E80-BA2F-4317-8368-37F8F4434D3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D819E80-BA2F-4317-8368-37F8F4434D3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D819E80-BA2F-4317-8368-37F8F4434D3A}.Release|Any CPU.Build.0 = Release|Any CPU {DB96C25F-39A9-4A6A-92BC-D1E42717308F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DB96C25F-39A9-4A6A-92BC-D1E42717308F}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB96C25F-39A9-4A6A-92BC-D1E42717308F}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1370,6 +1377,18 @@ Global {4E273CBC-BB1D-4AC1-91DB-C62FC83E0350}.Debug|Any CPU.Build.0 = Debug|Any CPU {4E273CBC-BB1D-4AC1-91DB-C62FC83E0350}.Release|Any CPU.ActiveCfg = Release|Any CPU {4E273CBC-BB1D-4AC1-91DB-C62FC83E0350}.Release|Any CPU.Build.0 = Release|Any CPU + {FCE88BBD-9BBD-4871-B9B0-DE176D73A6B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCE88BBD-9BBD-4871-B9B0-DE176D73A6B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCE88BBD-9BBD-4871-B9B0-DE176D73A6B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCE88BBD-9BBD-4871-B9B0-DE176D73A6B0}.Release|Any CPU.Build.0 = Release|Any CPU + {EDEF898A-CEFA-4151-8168-D0231A602093}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDEF898A-CEFA-4151-8168-D0231A602093}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDEF898A-CEFA-4151-8168-D0231A602093}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDEF898A-CEFA-4151-8168-D0231A602093}.Release|Any CPU.Build.0 = Release|Any CPU + {D817E3CE-F603-499B-B02A-7DECD017B170}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D817E3CE-F603-499B-B02A-7DECD017B170}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D817E3CE-F603-499B-B02A-7DECD017B170}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D817E3CE-F603-499B-B02A-7DECD017B170}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1534,7 +1553,6 @@ Global {686BF57E-A6FF-467B-AAB3-44DE916A9772} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {1DDE89EE-5819-441F-A060-2FF4A986F372} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {655A5B07-39B8-48CD-8590-8AC0C2B708D8} = {8977A560-45C2-4EC2-A849-97335B382C74} - {3D33BBFD-EC63-4E8C-A714-0A48A3809A87} = {BE25E872-1667-4649-9D19-96B83E75A44E} {BFFB5CAE-33B5-447E-9218-BDEBFDA96CB5} = {8977A560-45C2-4EC2-A849-97335B382C74} {FC32EF16-31B1-47B3-B625-A80933CB3F29} = {8977A560-45C2-4EC2-A849-97335B382C74} {453C8E28-81D4-431E-BFB0-F3D413346E51} = {8DBA5174-B0AA-4561-82B1-A46607697753} @@ -1619,11 +1637,16 @@ Global {4D9D7A28-BB44-4F3F-81DA-14F39B853718} = {CC126D03-7EAC-493F-B187-DCDEE1EF6A70} {5BABC440-4F1B-46E8-9068-DD7F02ED25D3} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} {5762E483-75CE-4328-A410-511F30737712} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} + {6D819E80-BA2F-4317-8368-37F8F4434D3A} = {8977A560-45C2-4EC2-A849-97335B382C74} {47D004BE-F797-430E-8A18-4B0CDFD56643} = {0F3118AE-8D36-4384-8E80-BD6566365305} {DB96C25F-39A9-4A6A-92BC-D1E42717308F} = {47D004BE-F797-430E-8A18-4B0CDFD56643} {64EADED3-4B5D-4431-BBE5-A4ABA1C38C00} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {730CADBA-701F-4722-9B6F-1FCC0DF2C95D} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {4E273CBC-BB1D-4AC1-91DB-C62FC83E0350} = {32A48625-F0AD-419D-828B-A50BDABA38EA} + {52ABB0E4-C3A1-4897-B51B-18EDA83F5D20} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC} + {FCE88BBD-9BBD-4871-B9B0-DE176D73A6B0} = {52ABB0E4-C3A1-4897-B51B-18EDA83F5D20} + {EDEF898A-CEFA-4151-8168-D0231A602093} = {52ABB0E4-C3A1-4897-B51B-18EDA83F5D20} + {D817E3CE-F603-499B-B02A-7DECD017B170} = {52ABB0E4-C3A1-4897-B51B-18EDA83F5D20} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} diff --git a/azure-pipelines-integration-corehost.yml b/azure-pipelines-integration-corehost.yml index 710a212c7e06c..fa6f2d32a9959 100644 --- a/azure-pipelines-integration-corehost.yml +++ b/azure-pipelines-integration-corehost.yml @@ -85,7 +85,6 @@ stages: configuration: Debug testRuns: - oop64bit: true - oopCoreClr: true lspEditor: false runName: VS_Integration_CoreHost_Debug @@ -98,6 +97,5 @@ stages: configuration: Release testRuns: - oop64bit: true - oopCoreClr: true lspEditor: false runName: VS_Integration_CoreHost_Release diff --git a/azure-pipelines-integration-lsp.yml b/azure-pipelines-integration-lsp.yml index 1176304615af8..2fc29141eb936 100644 --- a/azure-pipelines-integration-lsp.yml +++ b/azure-pipelines-integration-lsp.yml @@ -71,6 +71,5 @@ stages: configuration: Debug testRuns: - oop64bit: true - oopCoreClr: false lspEditor: true runName: VS_Integration_LSP_Debug_64 diff --git a/azure-pipelines-integration-scouting.yml b/azure-pipelines-integration-scouting.yml index edddff358055f..346f4dbfe0d38 100644 --- a/azure-pipelines-integration-scouting.yml +++ b/azure-pipelines-integration-scouting.yml @@ -48,11 +48,9 @@ stages: configuration: Debug testRuns: - oop64bit: false - oopCoreClr: false lspEditor: false runName: VS_Integration_Debug_32 - oop64bit: true - oopCoreClr: false lspEditor: false runName: VS_Integration_Debug_64 @@ -64,10 +62,8 @@ stages: configuration: Release testRuns: - oop64bit: false - oopCoreClr: false lspEditor: false runName: VS_Integration_Release_32 - oop64bit: true - oopCoreClr: false lspEditor: false runName: VS_Integration_Release_64 diff --git a/azure-pipelines-integration.yml b/azure-pipelines-integration.yml index 8e9e05ea61b22..b3945c448b11b 100644 --- a/azure-pipelines-integration.yml +++ b/azure-pipelines-integration.yml @@ -83,11 +83,9 @@ stages: testRuns: - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: - oop64bit: false - oopCoreClr: false lspEditor: false runName: VS_Integration_Debug_32 - oop64bit: true - oopCoreClr: false lspEditor: false runName: VS_Integration_Debug_64 @@ -99,11 +97,9 @@ stages: configuration: Release testRuns: - oop64bit: false - oopCoreClr: false lspEditor: false runName: VS_Integration_Release_32 - ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: - oop64bit: true - oopCoreClr: false lspEditor: false runName: VS_Integration_Release_64 diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 3b713dea8d779..1a86360a0fcb0 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -45,7 +45,7 @@ variables: value: .NETCoreValidation - group: DotNet-Roslyn-SDLValidation-Params - name: Codeql.Enabled - value: true​ + value: true # To retrieve OptProf data we need to authenticate to the VS drop storage. # Get access token with $dn-bot-devdiv-drop-rw-code-rw and dn-bot-dnceng-build-rw-code-rw from DotNet-VSTS-Infra-Access diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 08aa4304783ab..7ea4ad03c07e3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -55,9 +55,9 @@ variables: - name: UbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: - value: Build.Ubuntu.1804.Amd64.Open + value: Build.Ubuntu.2004.Amd64.Open ${{ else }}: - value: Build.Ubuntu.1804.Amd64 + value: Build.Ubuntu.2004.Amd64 - name: WindowsQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: @@ -79,9 +79,9 @@ variables: - name: HelixUbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: - value: Ubuntu.1804.Amd64.Open + value: Ubuntu.2004.Amd64.Open ${{ else }}: - value: Ubuntu.1804.Amd64 + value: Ubuntu.2004.Amd64 - name: HelixMacOsQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: @@ -148,15 +148,15 @@ stages: # Like template `eng/common/templates/jobs/source-build.yml` - job: Source_Build_Managed displayName: Source-Build (Managed) - container: mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8 + container: mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9 pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64 workspace: clean: all steps: @@ -164,7 +164,7 @@ stages: parameters: platform: name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' - stage: Windows_Debug_Desktop dependsOn: Windows_Debug_Build @@ -423,7 +423,7 @@ stages: steps: - template: eng/pipelines/checkout-windows-task.yml - - powershell: eng/make-bootstrap.ps1 -output $(bootstrapDir) + - powershell: eng/make-bootstrap.ps1 -output $(bootstrapDir) -ci displayName: Build Bootstrap Compiler - powershell: eng/test-determinism.ps1 -configuration Debug -bootstrapDir $(bootstrapDir) -ci diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 4fbf571e12e5b..3ba66cf3fe790 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -10,15 +10,16 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | -| [Ref Struct Interfaces](https://github.com/dotnet/csharplang/issues/7608) | [RefStructInterfaces](https://github.com/dotnet/roslyn/tree/features/RefStructInterfaces) | [In Progress](https://github.com/dotnet/roslyn/issues/72124) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) | +| Ref/unsafe in iterators/async | [RefInAsync](https://github.com/dotnet/roslyn/tree/features/RefInAsync) | [In Progress](https://github.com/dotnet/roslyn/issues/72662) | [jjonescz](https://github.com/jjonescz) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | [ToddGrun](https://github.com/ToddGrun) | | +| [Ref Struct Interfaces](https://github.com/dotnet/csharplang/issues/7608) | [RefStructInterfaces](https://github.com/dotnet/roslyn/tree/features/RefStructInterfaces) | [In Progress](https://github.com/dotnet/roslyn/issues/72124) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | [ToddGrun](https://github.com/ToddGrun) | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) | | [Semi-auto-properties](https://github.com/dotnet/csharplang/issues/140) | [semi-auto-props](https://github.com/dotnet/roslyn/tree/features/semi-auto-props) | [In Progress](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [Default in deconstruction](https://github.com/dotnet/roslyn/pull/25562) | [decon-default](https://github.com/dotnet/roslyn/tree/features/decon-default) | [In Progress](https://github.com/dotnet/roslyn/issues/25559) | [jcouv](https://github.com/jcouv) | [gafter](https://github.com/gafter) | | [jcouv](https://github.com/jcouv) | -| [Roles/Extensions](https://github.com/dotnet/csharplang/issues/5497) | [roles](https://github.com/dotnet/roslyn/tree/features/roles) | [In Progress](https://github.com/dotnet/roslyn/issues/66722) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [jjonescz](https://github.com/jjonescz) | | [MadsTorgersen](https://github.com/MadsTorgersen) | +| [Roles/Extensions](https://github.com/dotnet/csharplang/issues/5497) | [roles](https://github.com/dotnet/roslyn/tree/features/roles) | [In Progress](https://github.com/dotnet/roslyn/issues/66722) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [jjonescz](https://github.com/jjonescz) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [MadsTorgersen](https://github.com/MadsTorgersen) | | [Escape character](https://github.com/dotnet/csharplang/issues/7400) | N/A | [Merged into 17.9p1](https://github.com/dotnet/roslyn/pull/70497) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [Method group natural type improvements](https://github.com/dotnet/csharplang/blob/main/proposals/method-group-natural-type-improvements.md) | main | [Merged into 17.9p2](https://github.com/dotnet/roslyn/issues/69432) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | | [jcouv](https://github.com/jcouv) | | [`Lock` object](https://github.com/dotnet/csharplang/issues/7104) | [LockObject](https://github.com/dotnet/roslyn/tree/features/LockObject) | [Merged into 17.10p2](https://github.com/dotnet/roslyn/issues/71888) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [RikkiGibson](https://github.com/RikkiGibson) | | [stephentoub](https://github.com/stephentoub) | | Implicit indexer access in object initializers | main | [Merged into 17.9p3](https://github.com/dotnet/roslyn/pull/70649) | [jcouv](https://github.com/jcouv) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | | | -| [Params-collections](https://github.com/dotnet/csharplang/issues/7700) | main | [Merged to 17.10p3](https://github.com/dotnet/roslyn/issues/71137) | [AlekseyTs](https://github.com/AlekseyTs) | [RikkiGibson](https://github.com/RikkiGibson), [333fred](https://github.com/333fred) | | [MadsTorgersen](https://github.com/MadsTorgersen), [AlekseyTs](https://github.com/AlekseyTs) | +| [Params-collections](https://github.com/dotnet/csharplang/issues/7700) | main | [Merged to 17.10p3](https://github.com/dotnet/roslyn/issues/71137) | [AlekseyTs](https://github.com/AlekseyTs) | [RikkiGibson](https://github.com/RikkiGibson), [333fred](https://github.com/333fred) | [akhera99](https://github.com/akhera99) | [MadsTorgersen](https://github.com/MadsTorgersen), [AlekseyTs](https://github.com/AlekseyTs) | # C# 12.0 diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md index 21a6b0e2e4cb1..76b8741267d6e 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md @@ -35,7 +35,7 @@ public class C *Conversion* of a collection expression to a `struct` or `class` that implements `System.Collections.IEnumerable` and *does not* have a `CollectionBuilderAttribute` requires the target type to have an accessible constructor that can be called with no arguments and, if the collection expression is not empty, the target type must have an accessible `Add` method -that can be called with a single argument of [*iteration type*](https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/statements.md#1395-the-foreach-statement) of the target type. +that can be called with a single argument. Previously, the constructor and `Add` methods were required for *construction* of the collection instance but not for *conversion*. That meant the following call was ambiguous since both `char[]` and `string` were valid target types for the collection expression. @@ -47,24 +47,6 @@ static void Print(char[] arg) { } static void Print(string arg) { } ``` -Previously, the collection expression in `y = [1, 2, 3]` was allowed since construction only requires an applicable `Add` method for each element expression. -The collection expression is now an error because of the conversion requirement for an `Add` method than be called with an argument of the iteration type `object`. -```csharp -// ok: Add is not required for empty collection -MyCollection x = []; - -// error CS9215: Collection expression type must have an applicable instance or extension method 'Add' -// that can be called with an argument of iteration type 'object'. -// The best overloaded method is 'MyCollection.Add(int)'. -MyCollection y = [1, 2, 3]; - -class MyCollection : IEnumerable -{ - public void Add(int i) { ... } - IEnumerator IEnumerable.GetEnumerator() { ... } -} -``` - ## `ref` arguments can be passed to `in` parameters ***Introduced in Visual Studio 2022 version 17.8p2*** diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index ea4b42a8e1785..45df3f9cf844a 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -117,8 +117,8 @@ - - + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 262a1246aa41e..eb8c03c674abb 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -2,15 +2,15 @@ - + https://github.com/dotnet/source-build-externals - b46b7e6859f4094cd7f3e00dc0471d62f5d8d051 + bcd44732882bc2b81b30146c778eb6ccb7fea793 - + https://github.com/dotnet/source-build-reference-packages - 8d6e9cf10f64ff8fc02e434b516f6ca87c4b7215 + c0b5d69a1a1513528c77fffff708c7502d57c35c @@ -110,14 +110,14 @@ - + https://github.com/dotnet/arcade - 5c3fdd3b5aaaa32b24383ec12a60b37ebff13079 + fc2b7849b25c4a21457feb6da5fc7c9806a80976 - + https://github.com/dotnet/arcade - 5c3fdd3b5aaaa32b24383ec12a60b37ebff13079 + fc2b7849b25c4a21457feb6da5fc7c9806a80976 @@ -144,9 +144,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 5c3fdd3b5aaaa32b24383ec12a60b37ebff13079 + fc2b7849b25c4a21457feb6da5fc7c9806a80976 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index ca2c67bff31aa..e15afb3c04ac0 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -6,9 +6,9 @@ --> 4 - 10 + 11 0 - 3 + 1 $(MajorVersion).$(MinorVersion).$(PatchVersion) <_Audience Condition="'%(PkgDefBrokeredService.Audience)' == 'Process'">dword:00000001 <_Audience Condition="'%(PkgDefBrokeredService.Audience)' == 'RemoteExclusiveClient'">dword:00000100 @@ -231,7 +232,7 @@ <_PkgDefEntry Include="@(PkgDefBrokeredService)" Condition="'%(PkgDefBrokeredService.ProfferingPackageId)' == ''"> - - diff --git a/eng/targets/GenerateServiceHubConfigurationFiles.targets b/eng/targets/GenerateServiceHubConfigurationFiles.targets index a19cb268d1d0d..977eb704777ce 100644 --- a/eng/targets/GenerateServiceHubConfigurationFiles.targets +++ b/eng/targets/GenerateServiceHubConfigurationFiles.targets @@ -10,32 +10,9 @@ - - <_ServicesWithSuffix Include="@(ServiceHubService)" FileSuffix="64" HostIdSuffix="" /> - <_ServicesWithSuffix Include="@(ServiceHubService)" FileSuffix="64S" HostIdSuffix="S" /> - - - - - - - - - + <_ServicesWithSuffix Include="@(ServiceHubService)" FileSuffix="Core64" HostIdSuffix="" /> - <_ServicesWithSuffix Include="@(ServiceHubService)" FileSuffix="Core64S" HostIdSuffix="S" /> + <_ServicesWithSuffix Include="@(ServiceHubService)" FileSuffix="Core64S" HostIdSuffix="S" /> diff --git a/eng/targets/Imports.targets b/eng/targets/Imports.targets index 0fddfdc7a1a36..46eb0380702c3 100644 --- a/eng/targets/Imports.targets +++ b/eng/targets/Imports.targets @@ -208,6 +208,14 @@ + + + + + diff --git a/eng/targets/Services.props b/eng/targets/Services.props index 3712bf923d769..7a1dd4ad79358 100644 --- a/eng/targets/Services.props +++ b/eng/targets/Services.props @@ -44,6 +44,7 @@ + - - - diff --git a/eng/test-rebuild.ps1 b/eng/test-rebuild.ps1 index b6c52e0c349ec..05ebf1c658262 100644 --- a/eng/test-rebuild.ps1 +++ b/eng/test-rebuild.ps1 @@ -65,6 +65,10 @@ try { " --exclude net472\Zip\tools\vsixexpinstaller\System.ValueTuple.dll" + " --exclude net472\Zip\tools\vsixexpinstaller\VSIXExpInstaller.exe" + + # Semantic Search reference assemblies can't be reconstructed from source. + # The assemblies are not marked with ReferenceAssemblyAttribute attribute. + " --exclude net8.0\GeneratedRefAssemblies\Microsoft.CodeAnalysis.dll" + + " --debugPath `"$ArtifactsDir/BuildValidator`"" + " --sourcePath `"$RepoRoot/`"" + " --referencesPath `"$ArtifactsDir/bin`"" + diff --git a/global.json b/global.json index f323d2b0bb65e..c4eef31704d6b 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "xcopy-msbuild": "17.8.1-2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24161.1", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24161.1", + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24179.4", + "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24179.4", "Microsoft.Build.Traversal": "3.4.0" } } diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs index 9a08888a4ef41..a841e59061989 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/UseCollectionExpressionHelpers.cs @@ -5,12 +5,14 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -106,6 +108,10 @@ public static bool CanReplaceWithCollectionExpression( return false; } + var operation = semanticModel.GetOperation(topMostExpression, cancellationToken); + if (operation?.Parent is IAssignmentOperation { Type.TypeKind: TypeKind.Dynamic }) + return false; + // HACK: Workaround lack of compiler information for collection expression conversions with casts. // Specifically, hardcode in knowledge that a cast to a constructible collection type of the empty collection // expression will always succeed, and there's no need to actually validate semantics there. @@ -209,7 +215,16 @@ bool IsSafeConversionWhenTypesDoNotMatch(out bool changesSemantics) // `IEnumerable obj = Array.Empty();` or // `IEnumerable obj = new[] { "" };` if (IsWellKnownCollectionInterface(convertedType) && type.AllInterfaces.Contains(convertedType)) + { + // The observable collections are known to have significantly different behavior than List. So + // disallow converting those types to ensure semantics are preserved. We do this even though + // allowSemanticsChange is true because this will basically be certain to break semantics, as opposed to + // the more common case where semantics may change slightly, but likely not in a way that breaks code. + if (type.Name is nameof(ObservableCollection) or nameof(ReadOnlyObservableCollection)) + return false; + return true; + } // Implicit reference array conversion is acceptable if the user is ok with semantics changing. For example: // diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index a7a15178f83ab..b20cc6f8ba857 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + typeof se dá převést na nameof. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index b5ec0a85191d3..5af1d97c90635 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + "typeof" kann in "nameof" konvertiert werden diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index 7a51869b61fbc..10d8ff8bbc94a 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + "typeof" puede convertirse en "nameof" diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 2af2443407a99..240b308d2df21 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + « typeof » peut être converti en « nameof » diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index f3bb72c10ca99..77f9052b7fd2a 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + 'typeof' può essere convertito in 'nameof' diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index e775f0b6a2511..11fad2f8d6078 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + 'typeof' を 'nameof' に変換できます diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index 9bb8a212c10b9..b46c592342a82 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + 'typeof'를 'nameof'로 변환할 수 있습니다. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index 24ff89ea899fd..80aa29890c432 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + Element „typeof” można przekonwertować na element „nameof” diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index 11ae62d4bbae9..2299d1557e947 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + 'typeof' pode ser convertido em 'nameof' diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index aac6d9dc7df5d..102c9ed8b71a9 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + "typeof" можно преобразовать в "nameof". diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index 0d353386d64aa..1d49d918214ce 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + 'typeof' metodu 'nameof' metoduna dönüştürülebilir diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index 6affe14302f04..fb14afb9f4e93 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + “Typeof”可以转换为“nameof” diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index 8f83bfb1286d1..2460761cea7a4 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -454,7 +454,7 @@ 'typeof' can be converted to 'nameof' - 'typeof' can be converted to 'nameof' + 'typeof' 可轉換為 'nameof' diff --git a/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs index 039b5cd452721..835ec2a1a3653 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs @@ -24,7 +24,7 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeMemberRequired; [ExtensionOrder(Before = PredefinedCodeFixProviderNames.DeclareAsNullable)] internal sealed class CSharpMakeMemberRequiredCodeFixProvider : SyntaxEditorBasedCodeFixProvider { - private const string CS8618 = nameof(CS8618); // Non-nullable variable must contain a non-null value when exiting constructor. Consider declaring it as nullable. + private const string CS8618 = nameof(CS8618); // Non-nullable variable must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring it as nullable. [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/Analyzers/CSharp/CodeFixes/UsePrimaryConstructor/CSharpUsePrimaryConstructorCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UsePrimaryConstructor/CSharpUsePrimaryConstructorCodeFixProvider.cs index 5ff90d64cbd3a..7e4c133c3ee6f 100644 --- a/src/Analyzers/CSharp/CodeFixes/UsePrimaryConstructor/CSharpUsePrimaryConstructorCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UsePrimaryConstructor/CSharpUsePrimaryConstructorCodeFixProvider.cs @@ -238,38 +238,49 @@ ParameterListSyntax UpdateReferencesToNestedMembers(ParameterListSyntax paramete parameterList.DescendantNodes().OfType(), (nameSyntax, currentNameSyntax) => { - // Don't have to update if the member is already qualified. - - if (nameSyntax.Parent is not QualifiedNameSyntax qualifiedNameSyntax || - qualifiedNameSyntax.Left == nameSyntax) + if (nameSyntax.Parent is QualifiedNameSyntax qualifiedNameSyntax) { - // Qualified names occur in things like the `type` portion of the parameter - - // reference to a nested type in an unqualified fashion. Have to qualify this. - var symbol = semanticModel.GetSymbolInfo(nameSyntax, cancellationToken).GetAnySymbol(); - if (symbol is INamedTypeSymbol { ContainingType: { } containingType }) - return CreateDottedName(nameSyntax, currentNameSyntax, containingType); + // Don't have to update if the name is already the RHS of some qualified name. + if (qualifiedNameSyntax.Left == nameSyntax) + { + // Qualified names occur in things like the `type` portion of the parameter + return TryQualify(nameSyntax, currentNameSyntax); + } } - - if (nameSyntax.Parent is not MemberAccessExpressionSyntax memberAccessExpression || - memberAccessExpression.Expression == nameSyntax) + else if (nameSyntax.Parent is MemberAccessExpressionSyntax memberAccessExpression) { - // Member access expressions occur in things like the default initializer, or attribute - // arguments of the parameter. - - var symbol = semanticModel.GetSymbolInfo(nameSyntax, cancellationToken).GetAnySymbol(); - if (symbol is IMethodSymbol or IPropertySymbol or IEventSymbol or IFieldSymbol && - symbol is { ContainingType.OriginalDefinition: { } containingType } && - namedType.Equals(containingType)) + // Don't have to update if the name is already the RHS of some member access expr. + if (memberAccessExpression.Expression == nameSyntax) { - // reference to a member field an unqualified fashion. Have to qualify this. - return CreateDottedName(nameSyntax, currentNameSyntax, containingType); + // Member access expressions occur in things like the default initializer, or attribute + // arguments of the parameter. + return TryQualify(nameSyntax, currentNameSyntax); } } + else + { + // Standalone name. Try to qualify depending on if this is a type or member context. + return TryQualify(nameSyntax, currentNameSyntax); + } return currentNameSyntax; }); + SyntaxNode TryQualify( + SimpleNameSyntax originalName, + SimpleNameSyntax currentName) + { + var symbol = semanticModel.GetSymbolInfo(originalName, cancellationToken).GetAnySymbol(); + return symbol switch + { + INamedTypeSymbol { ContainingType: { } containingType } => CreateDottedName(originalName, currentName, containingType), + IMethodSymbol or IPropertySymbol or IEventSymbol or IFieldSymbol => + symbol is { ContainingType.OriginalDefinition: { } containingType } && + namedType.Equals(containingType) ? CreateDottedName(originalName, currentName, containingType) : currentName, + _ => currentName, + }; + } + SyntaxNode CreateDottedName( SimpleNameSyntax originalName, SimpleNameSyntax currentName, diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs index bd5d41f38c5ce..e52dfc7a0fd93 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs @@ -5179,4 +5179,191 @@ class C LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic1() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + obj.arr = new byte[] { 1, 2, 3 }; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic2() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + obj.arr = (new byte[] { 1, 2, 3 })!; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic3() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + obj = new byte[] { 1, 2, 3 }; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic4() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + Test(new byte[] { 1, 2, 3 }); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic5() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + Test((new byte[] { 1, 2, 3 })!); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic6() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + Test1(obj, new int?[] { 3 }); + } + + private void Test1(dynamic obj, params int?[][] args) + { + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72640")] + public async Task TestDynamic7() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + Test1(obj, [|[|new|] int?[]|] { 3 }); + } + + private void Test1(dynamic obj, int?[] args) + { + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + using System.Linq.Expressions; + + class C + { + public void Test(dynamic obj) + { + Test1(obj, [3]); + } + + private void Test1(dynamic obj, int?[] args) + { + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs index 37e3bdc1f4dac..76762105c8fce 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs @@ -5270,16 +5270,6 @@ public void Add(string s) { } { OutputKind = OutputKind.DynamicallyLinkedLibrary, }, - FixedState = - { - ExpectedDiagnostics = - { - // /0/Test0.cs(6,26): error CS1503: Argument 1: cannot convert from 'object' to 'string' - DiagnosticResult.CompilerError("CS1503").WithSpan(6, 26, 6, 36).WithArguments("1", "object", "string"), - // /0/Test0.cs(6,26): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(string)'. - DiagnosticResult.CompilerError("CS9215").WithSpan(6, 26, 6, 36).WithArguments("object", "MyCollection.Add(string)"), - } - } }.RunAsync(); } @@ -5356,4 +5346,324 @@ void M(int[] x) ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple1() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, [|new|] List()); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, []); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple2() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, [|new|] List() { }); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, []); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple3() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, [|new|] List { 1, 2, 3 }); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, [1, 2, 3]); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple4() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, [|new|] List + { + 1, + 2, + 3 + }); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, List) M() { + return (42, + [ + 1, + 2, + 3 + ]); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple5() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, Func>) M() { + return (42, () => [|new|] List()); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, Func>) M() { + return (42, () => []); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple6() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, Func>) M() { + return (42, () => [|new|] List() { 1, 2, 3 }); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, Func>) M() { + return (42, () => [1, 2, 3]); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72169")] + public async Task TestInValueTuple7() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, Func>) M() { + return (42, () => [|new|] List + { + 1, + 2, + 3 + }); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + + class C + { + public (int, Func>) M() { + return (42, () => + [ + 1, + 2, + 3 + ]); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72701")] + public async Task TestNotWithObservableCollection1() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class C + { + void M() + { + IList strings = new ObservableCollection(); + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72701")] + public async Task TestNotWithObservableCollection2() + { + await new VerifyCS.Test + { + TestCode = + """ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class C + { + void M() + { + ObservableCollection strings = [|new|] ObservableCollection(); + } + } + """, + FixedCode = + """ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + + class C + { + void M() + { + ObservableCollection strings = []; + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UsePrimaryConstructor/UsePrimaryConstructorTests.cs b/src/Analyzers/CSharp/Tests/UsePrimaryConstructor/UsePrimaryConstructorTests.cs index ad2fe39cb083e..7e695ec9d2089 100644 --- a/src/Analyzers/CSharp/Tests/UsePrimaryConstructor/UsePrimaryConstructorTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePrimaryConstructor/UsePrimaryConstructorTests.cs @@ -4028,4 +4028,47 @@ public C(Type type) ReferenceAssemblies = ReferenceAssemblies.Net.Net80, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72784")] + public async Task TestQualifyNestedEnum() + { + await new VerifyCS.Test + { + TestCode = """ + public class MyClass + { + public [|MyClass|](EnumInClass.MyEnum myEnum = EnumInClass.MyEnum.Default) + { + this.MyEnum = myEnum; + } + + public EnumInClass.MyEnum MyEnum { get; set; } + } + + public class EnumInClass + { + public enum MyEnum + { + Default + } + } + """, + FixedCode = """ + public class MyClass(EnumInClass.MyEnum myEnum = EnumInClass.MyEnum.Default) + { + public EnumInClass.MyEnum MyEnum { get; set; } = myEnum; + } + + public class EnumInClass + { + public enum MyEnum + { + Default + } + } + """, + LanguageVersion = LanguageVersion.CSharp12, + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 1275fa01322ef..002b0a25e6cb1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -368,7 +368,7 @@ private BoundIndexerAccess BindIndexerDefaultArgumentsAndParamsCollection(BoundI } } - BindDefaultArgumentsAndParamsCollection(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, namesBuilder, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics); + BindDefaultArguments(indexerAccess.Syntax, parameters, argumentsBuilder, refKindsBuilderOpt, namesBuilder, ref argsToParams, out defaultArguments, indexerAccess.Expanded, enableCallerInfo: true, diagnostics); if (namesBuilder is object) { @@ -3249,6 +3249,40 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres } return GetRefEscape(assignment.Left, scopeOfTheContainingExpression); + + case BoundKind.Conversion: + Debug.Assert(expr is BoundConversion conversion && + (!conversion.Conversion.IsUserDefined || + conversion.Conversion.Method.HasUnsupportedMetadata || + conversion.Conversion.Method.RefKind == RefKind.None)); + break; + + case BoundKind.UnaryOperator: + Debug.Assert(expr is BoundUnaryOperator unaryOperator && + (unaryOperator.MethodOpt is not { } unaryMethod || + unaryMethod.HasUnsupportedMetadata || + unaryMethod.RefKind == RefKind.None)); + break; + + case BoundKind.BinaryOperator: + Debug.Assert(expr is BoundBinaryOperator binaryOperator && + (binaryOperator.Method is not { } binaryMethod || + binaryMethod.HasUnsupportedMetadata || + binaryMethod.RefKind == RefKind.None)); + break; + + case BoundKind.UserDefinedConditionalLogicalOperator: + Debug.Assert(expr is BoundUserDefinedConditionalLogicalOperator logicalOperator && + (logicalOperator.LogicalOperator.HasUnsupportedMetadata || + logicalOperator.LogicalOperator.RefKind == RefKind.None)); + break; + + case BoundKind.CompoundAssignmentOperator: + Debug.Assert(expr is BoundCompoundAssignmentOperator compoundAssignmentOperator && + (compoundAssignmentOperator.Operator.Method is not { } compoundMethod || + compoundMethod.HasUnsupportedMetadata || + compoundMethod.RefKind == RefKind.None)); + break; } // At this point we should have covered all the possible cases for anything that is not a strict RValue. @@ -3587,6 +3621,37 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF { return CheckRefEscape(node, conversion.Operand, escapeFrom, escapeTo, checkingReceiver, diagnostics); } + + Debug.Assert(!conversion.Conversion.IsUserDefined || + conversion.Conversion.Method.HasUnsupportedMetadata || + conversion.Conversion.Method.RefKind == RefKind.None); + break; + + case BoundKind.UnaryOperator: + Debug.Assert(expr is BoundUnaryOperator unaryOperator && + (unaryOperator.MethodOpt is not { } unaryMethod || + unaryMethod.HasUnsupportedMetadata || + unaryMethod.RefKind == RefKind.None)); + break; + + case BoundKind.BinaryOperator: + Debug.Assert(expr is BoundBinaryOperator binaryOperator && + (binaryOperator.Method is not { } binaryMethod || + binaryMethod.HasUnsupportedMetadata || + binaryMethod.RefKind == RefKind.None)); + break; + + case BoundKind.UserDefinedConditionalLogicalOperator: + Debug.Assert(expr is BoundUserDefinedConditionalLogicalOperator logicalOperator && + (logicalOperator.LogicalOperator.HasUnsupportedMetadata || + logicalOperator.LogicalOperator.RefKind == RefKind.None)); + break; + + case BoundKind.CompoundAssignmentOperator: + Debug.Assert(expr is BoundCompoundAssignmentOperator compoundAssignmentOperator && + (compoundAssignmentOperator.Operator.Method is not { } compoundMethod || + compoundMethod.HasUnsupportedMetadata || + compoundMethod.RefKind == RefKind.None)); break; case BoundKind.ThrowExpression: @@ -3891,7 +3956,22 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres GetValEscape(withExpression.InitializerExpression, scopeOfTheContainingExpression)); case BoundKind.UnaryOperator: - return GetValEscape(((BoundUnaryOperator)expr).Operand, scopeOfTheContainingExpression); + var unaryOperator = (BoundUnaryOperator)expr; + if (unaryOperator.MethodOpt is { } unaryMethod) + { + return GetInvocationEscapeScope( + unaryMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + unaryMethod.Parameters, + argsOpt: [unaryOperator.Operand], + argRefKindsOpt: default, + argsToParamsOpt: default, + scopeOfTheContainingExpression: scopeOfTheContainingExpression, + isRefEscape: false); + } + + return GetValEscape(unaryOperator.Operand, scopeOfTheContainingExpression); case BoundKind.Conversion: var conversion = (BoundConversion)expr; @@ -3927,6 +4007,23 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres isRefEscape: false); } + if (conversion.Conversion.IsUserDefined) + { + var operatorMethod = conversion.Conversion.Method; + Debug.Assert(operatorMethod is not null); + + return GetInvocationEscapeScope( + operatorMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + operatorMethod.Parameters, + argsOpt: [conversion.Operand], + argRefKindsOpt: default, + argsToParamsOpt: default, + scopeOfTheContainingExpression: scopeOfTheContainingExpression, + isRefEscape: false); + } + return GetValEscape(conversion.Operand, scopeOfTheContainingExpression); case BoundKind.AssignmentOperator: @@ -3938,12 +4035,40 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.CompoundAssignmentOperator: var compound = (BoundCompoundAssignmentOperator)expr; + if (compound.Operator.Method is { } compoundMethod) + { + return GetInvocationEscapeScope( + compoundMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + compoundMethod.Parameters, + argsOpt: [compound.Left, compound.Right], + argRefKindsOpt: default, + argsToParamsOpt: default, + scopeOfTheContainingExpression: scopeOfTheContainingExpression, + isRefEscape: false); + } + return Math.Max(GetValEscape(compound.Left, scopeOfTheContainingExpression), GetValEscape(compound.Right, scopeOfTheContainingExpression)); case BoundKind.BinaryOperator: var binary = (BoundBinaryOperator)expr; + if (binary.Method is { } binaryMethod) + { + return GetInvocationEscapeScope( + binaryMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + binaryMethod.Parameters, + argsOpt: [binary.Left, binary.Right], + argRefKindsOpt: default, + argsToParamsOpt: default, + scopeOfTheContainingExpression: scopeOfTheContainingExpression, + isRefEscape: false); + } + return Math.Max(GetValEscape(binary.Left, scopeOfTheContainingExpression), GetValEscape(binary.Right, scopeOfTheContainingExpression)); @@ -3956,8 +4081,16 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres case BoundKind.UserDefinedConditionalLogicalOperator: var uo = (BoundUserDefinedConditionalLogicalOperator)expr; - return Math.Max(GetValEscape(uo.Left, scopeOfTheContainingExpression), - GetValEscape(uo.Right, scopeOfTheContainingExpression)); + return GetInvocationEscapeScope( + uo.LogicalOperator, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + uo.LogicalOperator.Parameters, + argsOpt: [uo.Left, uo.Right], + argRefKindsOpt: default, + argsToParamsOpt: default, + scopeOfTheContainingExpression: scopeOfTheContainingExpression, + isRefEscape: false); case BoundKind.QueryClause: return GetValEscape(((BoundQueryClause)expr).Value, scopeOfTheContainingExpression); @@ -4473,6 +4606,24 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.UnaryOperator: var unary = (BoundUnaryOperator)expr; + if (unary.MethodOpt is { } unaryMethod) + { + return CheckInvocationEscape( + unary.Syntax, + unaryMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + unaryMethod.Parameters, + argsOpt: [unary.Operand], + argRefKindsOpt: default, + argsToParamsOpt: default, + checkingReceiver: checkingReceiver, + escapeFrom: escapeFrom, + escapeTo: escapeTo, + diagnostics, + isRefEscape: false); + } + return CheckValEscape(node, unary.Operand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); case BoundKind.FromEndIndexExpression: @@ -4520,6 +4671,27 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF isRefEscape: false); } + if (conversion.Conversion.IsUserDefined) + { + var operatorMethod = conversion.Conversion.Method; + Debug.Assert(operatorMethod is not null); + + return CheckInvocationEscape( + conversion.Syntax, + operatorMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + operatorMethod.Parameters, + argsOpt: [conversion.Operand], + argRefKindsOpt: default, + argsToParamsOpt: default, + checkingReceiver: checkingReceiver, + escapeFrom: escapeFrom, + escapeTo: escapeTo, + diagnostics, + isRefEscape: false); + } + return CheckValEscape(node, conversion.Operand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); case BoundKind.AssignmentOperator: @@ -4533,6 +4705,24 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.CompoundAssignmentOperator: var compound = (BoundCompoundAssignmentOperator)expr; + if (compound.Operator.Method is { } compoundMethod) + { + return CheckInvocationEscape( + compound.Syntax, + compoundMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + compoundMethod.Parameters, + argsOpt: [compound.Left, compound.Right], + argRefKindsOpt: default, + argsToParamsOpt: default, + checkingReceiver: checkingReceiver, + escapeFrom: escapeFrom, + escapeTo: escapeTo, + diagnostics, + isRefEscape: false); + } + return CheckValEscape(compound.Left.Syntax, compound.Left, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) && CheckValEscape(compound.Right.Syntax, compound.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); @@ -4544,6 +4734,24 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF return true; } + if (binary.Method is { } binaryMethod) + { + return CheckInvocationEscape( + binary.Syntax, + binaryMethod, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + binaryMethod.Parameters, + argsOpt: [binary.Left, binary.Right], + argRefKindsOpt: default, + argsToParamsOpt: default, + checkingReceiver: checkingReceiver, + escapeFrom: escapeFrom, + escapeTo: escapeTo, + diagnostics, + isRefEscape: false); + } + return CheckValEscape(binary.Left.Syntax, binary.Left, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) && CheckValEscape(binary.Right.Syntax, binary.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); @@ -4560,8 +4768,20 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF case BoundKind.UserDefinedConditionalLogicalOperator: var uo = (BoundUserDefinedConditionalLogicalOperator)expr; - return CheckValEscape(uo.Left.Syntax, uo.Left, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics) && - CheckValEscape(uo.Right.Syntax, uo.Right, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); + return CheckInvocationEscape( + uo.Syntax, + uo.LogicalOperator, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, + uo.LogicalOperator.Parameters, + argsOpt: [uo.Left, uo.Right], + argRefKindsOpt: default, + argsToParamsOpt: default, + checkingReceiver: checkingReceiver, + escapeFrom: escapeFrom, + escapeTo: escapeTo, + diagnostics, + isRefEscape: false); case BoundKind.QueryClause: var clauseValue = ((BoundQueryClause)expr).Value; diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 6cd5b93fc0a6f..39e48112f4f15 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -17,27 +17,31 @@ namespace Microsoft.CodeAnalysis.CSharp { internal sealed partial class BinderFactory { - private sealed class BinderFactoryVisitor : CSharpSyntaxVisitor + internal sealed class BinderFactoryVisitor : CSharpSyntaxVisitor { private int _position; private CSharpSyntaxNode _memberDeclarationOpt; private Symbol _memberOpt; - private readonly BinderFactory _factory; + private BinderFactory _factory; - internal BinderFactoryVisitor(BinderFactory factory) - { - _factory = factory; - } - - internal void Initialize(int position, CSharpSyntaxNode memberDeclarationOpt, Symbol memberOpt) + internal void Initialize(BinderFactory factory, int position, CSharpSyntaxNode memberDeclarationOpt, Symbol memberOpt) { Debug.Assert((memberDeclarationOpt == null) == (memberOpt == null)); + _factory = factory; _position = position; _memberDeclarationOpt = memberDeclarationOpt; _memberOpt = memberOpt; } + internal void Clear() + { + _factory = null; + _position = 0; + _memberDeclarationOpt = null; + _memberOpt = null; + } + private CSharpCompilation compilation { get diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs index 7b588ae0fa4d1..b71380ca59616 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs @@ -17,7 +17,7 @@ internal sealed partial class BinderFactory { // key in the binder cache. // PERF: we are not using ValueTuple because its Equals is relatively slow. - private readonly struct BinderCacheKey : IEquatable + internal readonly struct BinderCacheKey : IEquatable { public readonly CSharpSyntaxNode syntaxNode; public readonly NodeUsage usage; @@ -55,15 +55,17 @@ public override bool Equals(object obj) // In a typing scenario, GetBinder is regularly called with a non-zero position. // This results in a lot of allocations of BinderFactoryVisitors. Pooling them // reduces this churn to almost nothing. + private static readonly ObjectPool s_binderFactoryVisitorPool + = new ObjectPool(static () => new BinderFactoryVisitor(), 64); + private readonly ObjectPool _binderFactoryVisitorPool; - internal BinderFactory(CSharpCompilation compilation, SyntaxTree syntaxTree, bool ignoreAccessibility) + internal BinderFactory(CSharpCompilation compilation, SyntaxTree syntaxTree, bool ignoreAccessibility, ObjectPool binderFactoryVisitorPoolOpt = null) { _compilation = compilation; _syntaxTree = syntaxTree; _ignoreAccessibility = ignoreAccessibility; - - _binderFactoryVisitorPool = new ObjectPool(() => new BinderFactoryVisitor(this), 64); + _binderFactoryVisitorPool = binderFactoryVisitorPoolOpt ?? s_binderFactoryVisitorPool; // 50 is more or less a guess, but it seems to work fine for scenarios that I tried. // we need something big enough to keep binders for most classes and some methods @@ -129,14 +131,27 @@ internal Binder GetBinder(SyntaxNode node, int position, CSharpSyntaxNode member container.AssertMemberExposure(memberOpt); } #endif - BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Initialize(position, memberDeclarationOpt, memberOpt); + BinderFactoryVisitor visitor = GetBinderFactoryVisitor(position, memberDeclarationOpt, memberOpt); Binder result = visitor.Visit(node); - _binderFactoryVisitorPool.Free(visitor); + ClearBinderFactoryVisitor(visitor); return result; } + private BinderFactoryVisitor GetBinderFactoryVisitor(int position, CSharpSyntaxNode memberDeclarationOpt, Symbol memberOpt) + { + BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); + visitor.Initialize(factory: this, position, memberDeclarationOpt, memberOpt); + + return visitor; + } + + private void ClearBinderFactoryVisitor(BinderFactoryVisitor visitor) + { + visitor.Clear(); + _binderFactoryVisitorPool.Free(visitor); + } + internal InMethodBinder GetPrimaryConstructorInMethodBinder(SynthesizedPrimaryConstructor constructor) { var typeDecl = constructor.GetSyntax(); @@ -159,10 +174,9 @@ internal InMethodBinder GetPrimaryConstructorInMethodBinder(SynthesizedPrimaryCo internal Binder GetInTypeBodyBinder(TypeDeclarationSyntax typeDecl) { - BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Initialize(position: typeDecl.SpanStart, memberDeclarationOpt: null, memberOpt: null); + BinderFactoryVisitor visitor = GetBinderFactoryVisitor(position: typeDecl.SpanStart, memberDeclarationOpt: null, memberOpt: null); Binder resultBinder = visitor.VisitTypeDeclarationCore(typeDecl, NodeUsage.NamedTypeBodyOrTypeParameters); - _binderFactoryVisitorPool.Free(visitor); + ClearBinderFactoryVisitor(visitor); return resultBinder; } @@ -174,20 +188,18 @@ internal Binder GetInNamespaceBinder(CSharpSyntaxNode unit) case SyntaxKind.NamespaceDeclaration: case SyntaxKind.FileScopedNamespaceDeclaration: { - BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Initialize(0, null, null); + BinderFactoryVisitor visitor = GetBinderFactoryVisitor(0, null, null); Binder result = visitor.VisitNamespaceDeclaration((BaseNamespaceDeclarationSyntax)unit, unit.SpanStart, inBody: true, inUsing: false); - _binderFactoryVisitorPool.Free(visitor); + ClearBinderFactoryVisitor(visitor); return result; } case SyntaxKind.CompilationUnit: // imports are bound by the Script class binder: { - BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); - visitor.Initialize(0, null, null); + BinderFactoryVisitor visitor = GetBinderFactoryVisitor(0, null, null); Binder result = visitor.VisitCompilationUnit((CompilationUnitSyntax)unit, inUsing: false, inScript: InScript); - _binderFactoryVisitorPool.Free(visitor); + ClearBinderFactoryVisitor(visitor); return result; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index fb59362df2573..1170b0c98b125 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -181,7 +181,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a Binder attributeArgumentBinder = this.WithAdditionalFlags(BinderFlags.AttributeArgument); AnalyzedAttributeArguments analyzedArguments = attributeArgumentBinder.BindAttributeArguments(argumentListOpt, attributeTypeForBinding, diagnostics); - ImmutableArray argsToParamsOpt = default; + ImmutableArray argsToParamsOpt; bool expanded = false; BitVector defaultArguments = default; MethodSymbol? attributeConstructor = null; @@ -191,6 +191,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a boundConstructorArguments = analyzedArguments.ConstructorArguments.Arguments.SelectAsArray( static (arg, attributeArgumentBinder) => attributeArgumentBinder.BindToTypeForErrorRecovery(arg), attributeArgumentBinder); + argsToParamsOpt = default; } else { @@ -210,12 +211,15 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments.ConstructorArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(node, memberResolutionResult, analyzedArguments.ConstructorArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argsToParamsOpt); + } + else + { + argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; } attributeConstructor = memberResolutionResult.Member; expanded = memberResolutionResult.Resolution == MemberResolutionKind.ApplicableInExpandedForm; - argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; if (!found) { @@ -229,7 +233,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a } else { - attributeArgumentBinder.BindDefaultArgumentsAndParamsCollection( + attributeArgumentBinder.BindDefaultArguments( node, attributeConstructor.Parameters, analyzedArguments.ConstructorArguments.Arguments, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 3d804f239c031..de77f9ff5d4cd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -944,7 +944,7 @@ private bool HasParamsCollectionTypeInProgress(NamedTypeSymbol toCheck) return false; } - internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, TypeSymbol elementType, out ImmutableArray addMethods, BindingDiagnosticBag diagnostics) + internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, out ImmutableArray addMethods, BindingDiagnosticBag diagnostics) { Debug.Assert(!targetType.IsDynamic()); @@ -958,7 +958,12 @@ internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, Type } var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; - var elementPlaceholder = new BoundValuePlaceholder(syntax, elementType) { WasCompilerGenerated = true }; + + // For the element, we create a dynamic argument and will be forcing overload resolution to convert it to any type. + // This way we are going to do most of the work in terms of determining applicability of 'Add' method candidates + // in overload resolution. + var elementPlaceholder = new BoundValuePlaceholder(syntax, Compilation.DynamicType) { WasCompilerGenerated = true }; + var addMethodBinder = WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod | BinderFlags.CollectionExpressionConversionValidation); if (namedType is not null) @@ -1047,20 +1052,6 @@ static bool bindInvocationExpression( diagnostics, out addMethods); } - // This is what BindDynamicInvocation is doing in terms of reporting diagnostics and detecting a failure - static bool bindDynamicInvocation( - Binder addMethodBinder, - SyntaxNode node, - BoundExpression? receiver, - AnalyzedArguments arguments, - BindingDiagnosticBag diagnostics) - { - ImmutableArray argArray = addMethodBinder.BuildArgumentsForDynamicInvocation(arguments, diagnostics); - var refKindsArray = arguments.RefKinds.ToImmutableOrNull(); - - return !ReportBadDynamicArguments(node, receiver, argArray, refKindsArray, diagnostics, queryClause: null); - } - // This is what BindMethodGroupInvocation is doing in terms of reporting diagnostics and detecting a failure static bool bindMethodGroupInvocation( Binder addMethodBinder, @@ -1071,12 +1062,15 @@ static bool bindMethodGroupInvocation( BindingDiagnosticBag diagnostics, out ImmutableArray addMethods) { + Debug.Assert(methodGroup.ReceiverOpt is not null); + Debug.Assert(methodGroup.ReceiverOpt.Type is not null); + bool result; CompoundUseSiteInfo useSiteInfo = addMethodBinder.GetNewCompoundUseSiteInfo(diagnostics); var resolution = addMethodBinder.ResolveMethodGroup( methodGroup, expression, WellKnownMemberNames.CollectionInitializerAddMethodName, analyzedArguments, useSiteInfo: ref useSiteInfo, - options: (analyzedArguments.HasDynamicArgument ? OverloadResolution.Options.DynamicResolution : OverloadResolution.Options.None)); + options: OverloadResolution.Options.DynamicResolution | OverloadResolution.Options.DynamicConvertsToAnything); diagnostics.Add(expression, useSiteInfo); @@ -1101,10 +1095,11 @@ static bool bindMethodGroupInvocation( } else { + Debug.Assert(resolution.AnalyzedArguments.HasDynamicArgument); + // If overload resolution found one or more applicable methods and at least one argument // was dynamic then treat this as a dynamic call. - if (resolution.AnalyzedArguments.HasDynamicArgument && - resolution.OverloadResolutionResult.HasAnyApplicableMember) + if (resolution.OverloadResolutionResult.HasAnyApplicableMember) { // Note that the runtime binder may consider candidates that haven't passed compile-time final validation // and an ambiguity error may be reported. Also additional checks are performed in runtime final validation @@ -1133,27 +1128,12 @@ static bool bindMethodGroupInvocation( { Debug.Assert(finalApplicableCandidates.Length > 0); - if (resolution.IsExtensionMethodGroup) - { - // error CS1973: 'T' has no applicable method named 'M' but appears to have an - // extension method by that name. Extension methods cannot be dynamically dispatched. Consider - // casting the dynamic arguments or calling the extension method without the extension method - // syntax. - - // We found an extension method, so the instance associated with the method group must have - // existed and had a type. - Debug.Assert(methodGroup.InstanceOpt?.Type is not null); - - Error(diagnostics, ErrorCode.ERR_BadArgTypeDynamicExtension, syntax, methodGroup.InstanceOpt.Type, methodGroup.Name); - addMethods = []; - result = false; - } - else - { - addMethodBinder.ReportDynamicInvocationWarnings(syntax, methodGroup, diagnostics, resolution, finalApplicableCandidates); + addMethods = filterOutBadGenericMethods(addMethodBinder, syntax, methodGroup, analyzedArguments, resolution, finalApplicableCandidates, ref useSiteInfo); + result = !addMethods.IsEmpty; - addMethods = finalApplicableCandidates.SelectAsArray(r => r.Member); - result = bindDynamicInvocation(addMethodBinder, syntax, methodGroup.ReceiverOpt, resolution.AnalyzedArguments, diagnostics); + if (!result) + { + diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, syntax, methodGroup.ReceiverOpt.Type); } } } @@ -1176,6 +1156,134 @@ static bool bindMethodGroupInvocation( return result; } + static ImmutableArray filterOutBadGenericMethods( + Binder addMethodBinder, SyntaxNode syntax, BoundMethodGroup methodGroup, AnalyzedArguments analyzedArguments, MethodGroupResolution resolution, + ImmutableArray> finalApplicableCandidates, ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(methodGroup.ReceiverOpt is not null); + var resultBuilder = ArrayBuilder.GetInstance(finalApplicableCandidates.Length); + + foreach (var candidate in finalApplicableCandidates) + { + // If the method is generic, skip it if the type arguments cannot be inferred. + var member = candidate.Member; + var typeParameters = member.TypeParameters; + + if (!typeParameters.IsEmpty) + { + if (resolution.IsExtensionMethodGroup) + { + // We need to validate an ability to infer type arguments as well as check conversion to 'this' parameter. + // Overload resolution doesn't check the conversion when 'this' type refers to a type parameter + TypeSymbol? receiverType = methodGroup.ReceiverOpt.Type; + Debug.Assert(receiverType is not null); + bool thisTypeIsOpen = typeParameters.Any((typeParameter, parameter) => parameter.Type.ContainsTypeParameter(typeParameter), member.Parameters[0]); + MethodSymbol? constructed = null; + bool wasFullyInferred = false; + + if (thisTypeIsOpen) + { + constructed = ReducedExtensionMethodSymbol.InferExtensionMethodTypeArguments( + member, receiverType, addMethodBinder.Compilation, ref useSiteInfo, out wasFullyInferred); + } + + if (constructed is null || !wasFullyInferred) + { + // It is quite possible that inference failed because we didn't supply type from the second argument + if (!typeParameters.Any((typeParameter, parameter) => parameter.Type.ContainsTypeParameter(typeParameter), member.Parameters[1])) + { + continue; + } + + // Let's attempt inference with type for the second parameter + // We are going to use the second parameter's type for that + OverloadResolution.GetEffectiveParameterTypes( + member, + argumentCount: 2, + argToParamMap: default, + argumentRefKinds: analyzedArguments.RefKinds, + isMethodGroupConversion: false, + allowRefOmittedArguments: methodGroup.ReceiverOpt.IsExpressionOfComImportType(), + binder: addMethodBinder, + expanded: candidate.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm, + parameterTypes: out ImmutableArray parameterTypes, + parameterRefKinds: out ImmutableArray parameterRefKinds); + + // If we were able to infer something just from the first parameter, + // use partially substituted second type, otherwise inference might fail + // for type parameters "shared" between the parameters. + TypeSymbol secondArgumentType = (constructed ?? member).Parameters[1].Type; + + MethodTypeInferenceResult inferenceResult = MethodTypeInferrer.Infer( + addMethodBinder, + addMethodBinder.Conversions, + member.TypeParameters, + member.ContainingType, + parameterTypes, + parameterRefKinds, + ImmutableArray.Create(methodGroup.ReceiverOpt, new BoundValuePlaceholder(syntax, secondArgumentType) { WasCompilerGenerated = true }), + ref useSiteInfo); + + if (!inferenceResult.Success) + { + continue; + } + + if (thisTypeIsOpen) + { + constructed = member.Construct(inferenceResult.InferredTypeArguments); + } + } + + if (thisTypeIsOpen) + { + Debug.Assert(constructed is not null); + var conversions = constructed.ContainingAssembly.CorLibrary.TypeConversions; + var conversion = conversions.ConvertExtensionMethodThisArg(constructed.Parameters[0].Type, receiverType, ref useSiteInfo); + if (!conversion.Exists) + { + continue; // Conversion to 'this' parameter failed + } + } + } + else if (typeParameters.Any((typeParameter, parameter) => !parameter.Type.ContainsTypeParameter(typeParameter), member.Parameters[0])) + { + // A type parameter does not appear in the parameter type. + continue; + } + } + + resultBuilder.Add(member); + } + + return resultBuilder.ToImmutableAndFree(); + } + + // This is what CanEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure + static bool canEarlyBindSingleCandidateInvocationWithDynamicArgument( + Binder addMethodBinder, + SyntaxNode syntax, + BoundMethodGroup boundMethodGroup, + BindingDiagnosticBag diagnostics, + MethodGroupResolution resolution, + MemberResolutionResult methodResolutionResult, + MethodSymbol singleCandidate) + { + Debug.Assert(boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty); + + if (singleCandidate.IsGenericMethod) + { + return false; + } + + if (addMethodBinder.IsAmbiguousDynamicParamsArgument(resolution.AnalyzedArguments.Arguments, methodResolutionResult, out SyntaxNode argumentSyntax)) + { + return false; + } + + return true; + } + // This is what TryEarlyBindSingleCandidateInvocationWithDynamicArgument is doing in terms of reporting diagnostics and detecting a failure static bool? tryEarlyBindSingleCandidateInvocationWithDynamicArgument( Binder addMethodBinder, @@ -1188,7 +1296,7 @@ static bool bindMethodGroupInvocation( out MethodSymbol? addMethod) { MethodSymbol singleCandidate = methodResolutionResult.LeastOverriddenMember; - if (!addMethodBinder.CanEarlyBindSingleCandidateInvocationWithDynamicArgument(syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate)) + if (!canEarlyBindSingleCandidateInvocationWithDynamicArgument(addMethodBinder, syntax, boundMethodGroup, diagnostics, resolution, methodResolutionResult, singleCandidate)) { addMethod = null; return null; @@ -1291,17 +1399,40 @@ static bool bindInvocationExpressionContinued( /// return the argument to the collection initializer Add method or null if the element is not a /// collection initializer node. Otherwise, return the element as is. /// - internal static BoundExpression? GetUnderlyingCollectionExpressionElement(BoundCollectionExpression expr, BoundExpression? element) + internal static BoundExpression GetUnderlyingCollectionExpressionElement(BoundCollectionExpression expr, BoundExpression element, bool throwOnErrors) { if (expr.CollectionTypeKind is CollectionExpressionTypeKind.ImplementsIEnumerable) { - return element switch + switch (element) { - BoundCollectionElementInitializer collectionInitializer => getCollectionInitializerElement(collectionInitializer), - BoundDynamicCollectionElementInitializer dynamicInitializer => dynamicInitializer.Arguments[0], - _ => null, - }; + case BoundCollectionElementInitializer collectionInitializer: + return getCollectionInitializerElement(collectionInitializer); + case BoundDynamicCollectionElementInitializer dynamicInitializer: + return dynamicInitializer.Arguments[0]; + } + + if (throwOnErrors) + { + throw ExceptionUtilities.UnexpectedValue(element); + } + + // Handle error cases from bindCollectionInitializerElementAddMethod. + switch (element) + { + case BoundCall call: + // Overload resolution failed with one or more applicable or ambiguous + // Add methods. This case can be hit for spreads and non-spread elements. + Debug.Assert(call.HasErrors); + Debug.Assert(call.Method.Name == "Add"); + return call.Arguments[call.InvokedAsExtensionMethod ? 1 : 0]; + case BoundBadExpression badExpression: + Debug.Assert(false); // Add test if we hit this assert. + return badExpression; + default: + throw ExceptionUtilities.UnexpectedValue(element); + } } + return element; static BoundExpression getCollectionInitializerElement(BoundCollectionElementInitializer collectionInitializer) @@ -1423,7 +1554,7 @@ private void GenerateImplicitConversionErrorForCollectionExpression( } if (elements.Length > 0 && - !HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, elementType, addMethods: out _, diagnostics)) + !HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, addMethods: out _, diagnostics)) { reportedErrors = true; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 3da3fa044ccb7..ce72e6c512004 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -3321,14 +3321,19 @@ private BoundExpression BindArgumentExpression(BindingDiagnosticBag diagnostics, #nullable enable private void CheckAndCoerceArguments( + SyntaxNode node, MemberResolutionResult methodResult, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics, BoundExpression? receiver, - bool invokedAsExtensionMethod) + bool invokedAsExtensionMethod, + out ImmutableArray argsToParamsOpt) where TMember : Symbol { var result = methodResult.Result; + bool expanded = result.Kind == MemberResolutionKind.ApplicableInExpandedForm; + int firstParamsArgument = -1; + ArrayBuilder? paramsArgsBuilder = null; var arguments = analyzedArguments.Arguments; // Parameter types should be taken from the least overridden member: @@ -3336,10 +3341,17 @@ private void CheckAndCoerceArguments( for (int arg = 0; arg < arguments.Count; ++arg) { - var kind = result.ConversionForArg(arg); BoundExpression argument = arguments[arg]; - if (argument is not BoundArgListOperator && !argument.HasAnyErrors) + if (argument is BoundArgListOperator) + { + Debug.Assert(result.ConversionForArg(arg).IsIdentity); + Debug.Assert(!argument.NeedsToBeConverted()); + Debug.Assert(!expanded || result.ParameterFromArgument(arg) != parameters.Length - 1); + continue; + } + + if (!argument.HasAnyErrors) { var argRefKind = analyzedArguments.RefKind(arg); @@ -3348,7 +3360,7 @@ private void CheckAndCoerceArguments( // Disallow using `ref readonly` parameters with no or `in` argument modifier, // same as older versions of the compiler would (since they would see the parameter as `ref`). if (argRefKind is RefKind.None or RefKind.In && - GetCorrespondingParameter(ref result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) + getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) { var available = CheckFeatureAvailability(argument.Syntax, MessageID.IDS_FeatureRefReadonlyParameters, diagnostics); Debug.Assert(!available); @@ -3361,7 +3373,7 @@ private void CheckAndCoerceArguments( // Warn for `ref`/`in` or None/`ref readonly` mismatch. if (argRefKind == RefKind.Ref) { - if (GetCorrespondingParameter(ref result, parameters, arg).RefKind == RefKind.In) + if (getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.In) { Debug.Assert(argNumber > 0); // The 'ref' modifier for argument {0} corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. @@ -3372,7 +3384,7 @@ private void CheckAndCoerceArguments( } } else if (argRefKind == RefKind.None && - GetCorrespondingParameter(ref result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) + getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) { if (!this.CheckValueKind(argument.Syntax, argument, BindValueKind.RefersToLocation, checkingReceiver: false, BindingDiagnosticBag.Discarded)) { @@ -3407,51 +3419,196 @@ private void CheckAndCoerceArguments( } } + int paramNum = result.ParameterFromArgument(arg); + + if (expanded && paramNum == parameters.Length - 1) + { + Debug.Assert(paramsArgsBuilder is null); + firstParamsArgument = arg; + paramsArgsBuilder = collectParamsArgs(in methodResult, parameters, arguments, ref arg, diagnostics); + continue; + } + + arguments[arg] = coerceArgument(in methodResult, receiver, parameters, argumentsForInterpolationConversion: arguments, argument, arg, parameters[paramNum].TypeWithAnnotations, diagnostics); + } + + argsToParamsOpt = result.ArgsToParamsOpt; + + if (paramsArgsBuilder is not null) + { + // Note, this call is going to free paramsArgsBuilder + createParamsCollection(node, in methodResult, receiver, parameters, analyzedArguments, firstParamsArgument, paramsArgsBuilder, ref argsToParamsOpt, diagnostics); + } + + Debug.Assert(analyzedArguments.RefKinds.Count == 0 || analyzedArguments.RefKinds.Count == arguments.Count); + Debug.Assert(analyzedArguments.Names.Count == 0 || analyzedArguments.Names.Count == arguments.Count); + Debug.Assert(argsToParamsOpt.IsDefault || argsToParamsOpt.Length == arguments.Count); + + result.ArgumentsWereCoerced(); + return; + + BoundExpression coerceArgument( + in MemberResolutionResult methodResult, + BoundExpression? receiver, + ImmutableArray parameters, + ArrayBuilder? argumentsForInterpolationConversion, + BoundExpression argument, + int arg, + TypeWithAnnotations parameterTypeWithAnnotations, + BindingDiagnosticBag diagnostics) + { + var result = methodResult.Result; + var kind = result.ConversionForArg(arg); + BoundExpression coercedArgument = argument; + if (kind.IsInterpolatedStringHandler) { Debug.Assert(argument is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations); - arguments[arg] = BindInterpolatedStringHandlerInMemberCall(argument, parameterTypeWithAnnotations.Type, arguments, parameters, ref result, arg, receiver, diagnostics); + coercedArgument = bindInterpolatedStringHandlerInMemberCall(argument, parameterTypeWithAnnotations.Type, argumentsForInterpolationConversion, parameters, in result, arg, receiver, diagnostics); } // https://github.com/dotnet/roslyn/issues/37119 : should we create an (Identity) conversion when the kind is Identity but the types differ? else if (!kind.IsIdentity) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations); - arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); + coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); } else if (argument.Kind == BoundKind.OutVariablePendingInference) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); - arguments[arg] = ((OutVariablePendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, diagnostics); + coercedArgument = ((OutVariablePendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, diagnostics); } else if (argument.Kind == BoundKind.OutDeconstructVarPendingInference) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); - arguments[arg] = ((OutDeconstructVarPendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, success: true); + coercedArgument = ((OutDeconstructVarPendingInference)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations, success: true); } else if (argument.Kind == BoundKind.DiscardExpression && !argument.HasExpressionType()) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); Debug.Assert(parameterTypeWithAnnotations.HasType); - arguments[arg] = ((BoundDiscardExpression)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations); + coercedArgument = ((BoundDiscardExpression)argument).SetInferredTypeWithAnnotations(parameterTypeWithAnnotations); } else if (argument.NeedsToBeConverted()) { Debug.Assert(kind.IsIdentity); if (argument is BoundTupleLiteral) { - TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); // CreateConversion reports tuple literal name mismatches, and constructs the expected pattern of bound nodes. - arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); + coercedArgument = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); } else { - arguments[arg] = BindToNaturalType(argument, diagnostics); + coercedArgument = BindToNaturalType(argument, diagnostics); } } + + return coercedArgument; + } + + static ArrayBuilder collectParamsArgs( + in MemberResolutionResult methodResult, + ImmutableArray parameters, + ArrayBuilder arguments, + ref int arg, + BindingDiagnosticBag diagnostics) + { + var result = methodResult.Result; + var paramsArgsBuilder = ArrayBuilder.GetInstance(); + int paramsIndex = parameters.Length - 1; + + while (true) + { + Debug.Assert(arguments[arg].Kind is not + (BoundKind.OutVariablePendingInference or BoundKind.OutDeconstructVarPendingInference or BoundKind.DiscardExpression or BoundKind.ArgListOperator)); + + // Conversions to elements of collection are applied in the process of collection construction + paramsArgsBuilder.Add(arguments[arg]); + + if (arg + 1 == arguments.Count || result.ParameterFromArgument(arg + 1) != paramsIndex) + { + break; + } + + arg++; + } + + return paramsArgsBuilder; + } + + // Note, this function is going to free paramsArgsBuilder + void createParamsCollection( + SyntaxNode node, + in MemberResolutionResult methodResult, + BoundExpression? receiver, + ImmutableArray parameters, + AnalyzedArguments analyzedArguments, + int firstParamsArgument, + ArrayBuilder paramsArgsBuilder, + ref ImmutableArray argsToParamsOpt, + BindingDiagnosticBag diagnostics) + { + Debug.Assert(methodResult.Result.ParamsElementTypeOpt.HasType); + Debug.Assert(methodResult.Result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); + + int paramsIndex = parameters.Length - 1; + + if (parameters[paramsIndex].Type.IsSZArray()) + { + var result = methodResult.Result; + TypeWithAnnotations paramsElementTypeOpt = result.ParamsElementTypeOpt; + + for (int i = 0; i < paramsArgsBuilder.Count; i++) + { + paramsArgsBuilder[i] = coerceArgument( + in methodResult, receiver, parameters, + argumentsForInterpolationConversion: null, // We do not use arguments for interpolations as param array elements + paramsArgsBuilder[i], + arg: firstParamsArgument + i, + paramsElementTypeOpt, + diagnostics); + } + } + + ImmutableArray collectionArgs = paramsArgsBuilder.ToImmutableAndFree(); + Debug.Assert(collectionArgs.Length != 0); + + BoundExpression collection = CreateParamsCollection(node, parameters[paramsIndex], collectionArgs, diagnostics); + var arguments = analyzedArguments.Arguments; + + Debug.Assert(firstParamsArgument != -1); + Debug.Assert(collectionArgs.Length == 1 || firstParamsArgument + collectionArgs.Length == arguments.Count); + + ArrayBuilder? argsToParamsBuilder = null; + if (!argsToParamsOpt.IsDefault && collectionArgs.Length > 1) + { + argsToParamsBuilder = ArrayBuilder.GetInstance(argsToParamsOpt.Length); + argsToParamsBuilder.AddRange(argsToParamsOpt); + } + + for (var i = firstParamsArgument + collectionArgs.Length - 1; i != firstParamsArgument; i--) + { + arguments.RemoveAt(i); + + Debug.Assert(argsToParamsBuilder is not null || argsToParamsOpt.IsDefault); + argsToParamsBuilder?.RemoveAt(i); + + if (analyzedArguments.RefKinds is { Count: > 0 } refKindsBuilder) + { + refKindsBuilder.RemoveAt(i); + } + + if (analyzedArguments.Names is { Count: > 0 } namesBuilder) + { + namesBuilder.RemoveAt(i); + } + } + + arguments[firstParamsArgument] = collection; + + if (argsToParamsBuilder is object) + { + argsToParamsOpt = argsToParamsBuilder.ToImmutableOrNull(); + argsToParamsBuilder.Free(); + } } void reportUnsafeIfNeeded(MemberResolutionResult methodResult, BindingDiagnosticBag diagnostics, BoundExpression argument, TypeWithAnnotations parameterTypeWithAnnotations) @@ -3464,28 +3621,238 @@ void reportUnsafeIfNeeded(MemberResolutionResult methodResult, BindingD //CONSIDER: Return a bad expression so that HasErrors is true? } } - } - - private static ParameterSymbol GetCorrespondingParameter(ref MemberAnalysisResult result, ImmutableArray parameters, int arg) - { - int paramNum = result.ParameterFromArgument(arg); - return parameters[paramNum]; - } -#nullable disable - - private static TypeWithAnnotations GetCorrespondingParameterTypeWithAnnotations(ref MemberAnalysisResult result, ImmutableArray parameters, int arg) - { - int paramNum = result.ParameterFromArgument(arg); - if (paramNum == parameters.Length - 1 && result.Kind == MemberResolutionKind.ApplicableInExpandedForm) + static ParameterSymbol getCorrespondingParameter(in MemberAnalysisResult result, ImmutableArray parameters, int arg) { - Debug.Assert(result.ParamsElementTypeOpt.HasType); - Debug.Assert(result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); - return result.ParamsElementTypeOpt; + int paramNum = result.ParameterFromArgument(arg); + return parameters[paramNum]; } - return parameters[paramNum].TypeWithAnnotations; + BoundExpression bindInterpolatedStringHandlerInMemberCall( + BoundExpression unconvertedString, + TypeSymbol handlerType, + ArrayBuilder? arguments, + ImmutableArray parameters, + in MemberAnalysisResult memberAnalysisResult, + int interpolatedStringArgNum, + BoundExpression? receiver, + BindingDiagnosticBag diagnostics) + { + Debug.Assert(unconvertedString is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); + var interpolatedStringConversion = memberAnalysisResult.ConversionForArg(interpolatedStringArgNum); + Debug.Assert(interpolatedStringConversion.IsInterpolatedStringHandler); + Debug.Assert(handlerType is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); + + var correspondingParameter = getCorrespondingParameter(in memberAnalysisResult, parameters, interpolatedStringArgNum); + var handlerParameterIndexes = correspondingParameter.InterpolatedStringHandlerArgumentIndexes; + + if (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && correspondingParameter.Ordinal == parameters.Length - 1) + { + Debug.Assert(handlerParameterIndexes.IsEmpty); + + // No arguments, fall back to the standard conversion steps. + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + handlerType, + diagnostics); + } + + Debug.Assert(arguments is not null); + + if (correspondingParameter.HasInterpolatedStringHandlerArgumentError) + { + // The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, unconvertedString.Syntax.Location, correspondingParameter, handlerType); + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + wasCompilerGenerated: false, + handlerType, + diagnostics, + hasErrors: true); + } + + if (handlerParameterIndexes.IsEmpty) + { + // No arguments, fall back to the standard conversion steps. + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + handlerType, + diagnostics); + } + + Debug.Assert(handlerParameterIndexes.All((index, paramLength) => index >= BoundInterpolatedStringArgumentPlaceholder.InstanceParameter && index < paramLength, + parameters.Length)); + + // We need to find the appropriate argument expression for every expected parameter, and error on any that occur after the current parameter + + ImmutableArray handlerArgumentIndexes; + + if (memberAnalysisResult.ArgsToParamsOpt.IsDefault && arguments.Count == parameters.Length) + { + // No parameters are missing and no remapped indexes, we can just use the original indexes + handlerArgumentIndexes = handlerParameterIndexes; + } + else + { + // Args and parameters were reordered via named parameters, or parameters are missing. Find the correct argument index for each parameter. + var handlerArgumentIndexesBuilder = ArrayBuilder.GetInstance(handlerParameterIndexes.Length, fillWithValue: BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); + for (int handlerParameterIndex = 0; handlerParameterIndex < handlerParameterIndexes.Length; handlerParameterIndex++) + { + int handlerParameter = handlerParameterIndexes[handlerParameterIndex]; + Debug.Assert(handlerArgumentIndexesBuilder[handlerParameterIndex] is BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); + + if (handlerParameter == BoundInterpolatedStringArgumentPlaceholder.InstanceParameter) + { + handlerArgumentIndexesBuilder[handlerParameterIndex] = handlerParameter; + continue; + } + + for (int argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) + { + // The index in the original parameter list we're looking to match up. + int argumentParameterIndex = memberAnalysisResult.ParameterFromArgument(argumentIndex); + // Is the original parameter index of the current argument the parameter index that was specified in the attribute? + if (argumentParameterIndex == handlerParameter) + { + // We can't just bail out on the first match: users can duplicate parameters in attributes, causing the same value to be passed twice. + handlerArgumentIndexesBuilder[handlerParameterIndex] = argumentIndex; + } + } + } + + handlerArgumentIndexes = handlerArgumentIndexesBuilder.ToImmutableAndFree(); + } + + var argumentPlaceholdersBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); + var argumentRefKindsBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); + bool hasErrors = false; + + // Now, go through all the specified arguments and see if any were specified _after_ the interpolated string, and construct + // a set of placeholders for overload resolution. + for (int i = 0; i < handlerArgumentIndexes.Length; i++) + { + int argumentIndex = handlerArgumentIndexes[i]; + Debug.Assert(argumentIndex != interpolatedStringArgNum); + + RefKind refKind; + TypeSymbol placeholderType; + switch (argumentIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + Debug.Assert(receiver!.Type is not null); + refKind = RefKind.None; + placeholderType = receiver.Type; + break; + case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: + { + // Don't error if the parameter isn't optional or params: the user will already have an error for missing an optional parameter or overload resolution failed. + // If it is optional, then they could otherwise not specify the parameter and that's an error + var originalParameterIndex = handlerParameterIndexes[i]; + var parameter = parameters[originalParameterIndex]; + if (parameter.IsOptional || + (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && originalParameterIndex + 1 == parameters.Length)) + { + // Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + diagnostics.Add( + ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, + unconvertedString.Syntax.Location, + parameter.Name, + correspondingParameter.Name); + hasErrors = true; + } + + refKind = parameter.RefKind; + placeholderType = parameter.Type; + } + break; + default: + { + var originalParameterIndex = handlerParameterIndexes[i]; + var parameter = parameters[originalParameterIndex]; + if (argumentIndex > interpolatedStringArgNum) + { + // Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + diagnostics.Add( + ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, + arguments[argumentIndex].Syntax.Location, + parameter.Name, + correspondingParameter.Name); + hasErrors = true; + } + + refKind = parameter.RefKind; + placeholderType = parameter.Type; + } + break; + } + + SyntaxNode placeholderSyntax; + bool isSuppressed; + + switch (argumentIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + Debug.Assert(receiver != null); + isSuppressed = receiver.IsSuppressed; + placeholderSyntax = receiver.Syntax; + break; + case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: + placeholderSyntax = unconvertedString.Syntax; + isSuppressed = false; + break; + case >= 0: + placeholderSyntax = arguments[argumentIndex].Syntax; + isSuppressed = arguments[argumentIndex].IsSuppressed; + break; + default: + throw ExceptionUtilities.UnexpectedValue(argumentIndex); + } + + argumentPlaceholdersBuilder.Add( + (BoundInterpolatedStringArgumentPlaceholder)(new BoundInterpolatedStringArgumentPlaceholder( + placeholderSyntax, + argumentIndex, + placeholderType, + hasErrors: argumentIndex == BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter) + { WasCompilerGenerated = true }.WithSuppression(isSuppressed))); + // We use the parameter refkind, rather than what the argument was actually passed with, because that will suppress duplicated errors + // about arguments being passed with the wrong RefKind. The user will have already gotten an error about mismatched RefKinds or it will + // be a place where refkinds are allowed to differ + argumentRefKindsBuilder.Add(refKind == RefKind.RefReadOnlyParameter ? RefKind.In : refKind); + } + + var interpolatedString = BindUnconvertedInterpolatedExpressionToHandlerType( + unconvertedString, + (NamedTypeSymbol)handlerType, + diagnostics, + additionalConstructorArguments: argumentPlaceholdersBuilder.ToImmutableAndFree(), + additionalConstructorRefKinds: argumentRefKindsBuilder.ToImmutableAndFree()); + + return new BoundConversion( + interpolatedString.Syntax, + interpolatedString, + interpolatedStringConversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: false, + conversionGroupOpt: null, + constantValueOpt: null, + handlerType, + hasErrors || interpolatedString.HasErrors); + } } +#nullable disable private BoundExpression BindArrayCreationExpression(ArrayCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { @@ -4536,9 +4903,15 @@ private BoundExpression BindConstructorInitializerCoreContinued( { ReportConstructorUseSiteDiagnostics(errorLocation, diagnostics, suppressUnsupportedRequiredMembersError: true, in overloadResolutionUseSiteInfo); + ImmutableArray argsToParamsOpt; + if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(nonNullSyntax, memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argsToParamsOpt); + } + else + { + argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; } NamedTypeSymbol baseType = constructor.ContainingType.BaseTypeNoUseSiteDiagnostics; @@ -4577,7 +4950,6 @@ private BoundExpression BindConstructorInitializerCoreContinued( ReportDiagnosticsIfObsolete(diagnostics, resultMember, nonNullSyntax, hasBaseReceiver: isBaseConstructorInitializer); var expanded = memberResolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt; if (constructor is SynthesizedPrimaryConstructor primaryConstructor) { @@ -4615,7 +4987,7 @@ private BoundExpression BindConstructorInitializerCoreContinued( primaryConstructor.SetParametersPassedToTheBase(parametersPassedToBase); } - BindDefaultArgumentsAndParamsCollection(nonNullSyntax, resultMember.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParamsOpt, out var defaultArguments, expanded, enableCallerInfo, diagnostics); + BindDefaultArguments(nonNullSyntax, resultMember.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParamsOpt, out var defaultArguments, expanded, enableCallerInfo, diagnostics); var arguments = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); @@ -5933,7 +6305,7 @@ boundElementInitializerExpressions[0] is not var d = BindingDiagnosticBag.GetInstance(); // This assert provides some validation that, if the real invocation binding succeeds, then the HasCollectionExpressionApplicableAddMethod helper succeeds as well. - Debug.Assert(collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, boundElementInitializerExpressions[0].Type, addMethods: out _, d)); + Debug.Assert(collectionInitializerAddMethodBinder.HasCollectionExpressionApplicableAddMethod(elementInitializer, implicitReceiver.Type, addMethods: out _, d)); d.Free(); } @@ -6303,9 +6675,15 @@ private BoundObjectCreationExpression BindClassCreationExpressionContinued( ReportConstructorUseSiteDiagnostics(typeNode.Location, diagnostics, suppressUnsupportedRequiredMembersError: false, in overloadResolutionUseSiteInfo); + ImmutableArray argToParams; + if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(node, memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, out argToParams); + } + else + { + argToParams = memberResolutionResult.Result.ArgsToParamsOpt; } var method = memberResolutionResult.Member; @@ -6331,8 +6709,7 @@ private BoundObjectCreationExpression BindClassCreationExpressionContinued( null; var expanded = memberResolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argToParams = memberResolutionResult.Result.ArgsToParamsOpt; - BindDefaultArgumentsAndParamsCollection(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); + BindDefaultArguments(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); var arguments = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); @@ -6383,7 +6760,7 @@ private BoundExpression CreateBadClassCreationExpression( if (memberResolutionResult.IsNotNull) { - this.CheckAndCoerceArguments(memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + this.CheckAndCoerceArguments(node, memberResolutionResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, argsToParamsOpt: out _); } LookupResultKind resultKind; @@ -7919,7 +8296,8 @@ protected MethodGroupResolution BindExtensionMethod( OverloadResolution.Options.IsFunctionPointerResolution | OverloadResolution.Options.InferWithDynamic | OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter | - OverloadResolution.Options.DynamicResolution)) == 0); + OverloadResolution.Options.DynamicResolution | + OverloadResolution.Options.DynamicConvertsToAnything)) == 0); var firstResult = new MethodGroupResolution(); AnalyzedArguments actualArguments = null; @@ -9433,9 +9811,6 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( MemberResolutionResult resolutionResult = overloadResolutionResult.ValidResult; PropertySymbol property = resolutionResult.Member; - var isExpanded = resolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParams = resolutionResult.Result.ArgsToParamsOpt; - ReportDiagnosticsIfObsolete(diagnostics, property, syntax, hasBaseReceiver: receiver != null && receiver.Kind == BoundKind.BaseReference); // Make sure that the result of overload resolution is valid. @@ -9443,7 +9818,8 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( receiver = ReplaceTypeOrValueReceiver(receiver, property.IsStatic, diagnostics); - this.CheckAndCoerceArguments(resolutionResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: false); + ImmutableArray argsToParams; + this.CheckAndCoerceArguments(syntax, resolutionResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: false, out argsToParams); if (!gotError && receiver != null && receiver.Kind == BoundKind.ThisReference && receiver.WasCompilerGenerated) { @@ -9462,7 +9838,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccessContinued( arguments, argumentNames, argumentRefKinds, - isExpanded, + expanded: resolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm, argsToParams, defaultArguments: default, property.Type, @@ -9980,7 +10356,8 @@ private MethodGroupResolution ResolveDefaultMethodGroup( OverloadResolution.Options.IsFunctionPointerResolution | OverloadResolution.Options.InferWithDynamic | OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter | - OverloadResolution.Options.DynamicResolution)) == 0); + OverloadResolution.Options.DynamicResolution | + OverloadResolution.Options.DynamicConvertsToAnything)) == 0); var methods = node.Methods; if (methods.Length == 0) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs index c5a27ffff11b7..0a15fecb093c9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs @@ -873,227 +873,5 @@ private ImmutableArray BindInterpolatedStringParts(BoundUnconve positionInfo.Free(); return (builderAppendCallsArray.ToImmutableAndFree(), builderPatternExpectsBool ?? false, positionInfoArray.ToImmutableAndFree(), baseStringLength, numFormatHoles); } - - private BoundExpression BindInterpolatedStringHandlerInMemberCall( - BoundExpression unconvertedString, - TypeSymbol handlerType, - ArrayBuilder arguments, - ImmutableArray parameters, - ref MemberAnalysisResult memberAnalysisResult, - int interpolatedStringArgNum, - BoundExpression? receiver, - BindingDiagnosticBag diagnostics) - { - Debug.Assert(unconvertedString is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); - var interpolatedStringConversion = memberAnalysisResult.ConversionForArg(interpolatedStringArgNum); - Debug.Assert(interpolatedStringConversion.IsInterpolatedStringHandler); - Debug.Assert(handlerType is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); - - var correspondingParameter = GetCorrespondingParameter(ref memberAnalysisResult, parameters, interpolatedStringArgNum); - var handlerParameterIndexes = correspondingParameter.InterpolatedStringHandlerArgumentIndexes; - - if (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && correspondingParameter.Ordinal == parameters.Length - 1) - { - Debug.Assert(handlerParameterIndexes.IsEmpty); - - // No arguments, fall back to the standard conversion steps. - return CreateConversion( - unconvertedString.Syntax, - unconvertedString, - interpolatedStringConversion, - isCast: false, - conversionGroupOpt: null, - handlerType, - diagnostics); - } - - if (correspondingParameter.HasInterpolatedStringHandlerArgumentError) - { - // The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. - diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, unconvertedString.Syntax.Location, correspondingParameter, handlerType); - return CreateConversion( - unconvertedString.Syntax, - unconvertedString, - interpolatedStringConversion, - isCast: false, - conversionGroupOpt: null, - wasCompilerGenerated: false, - handlerType, - diagnostics, - hasErrors: true); - } - - if (handlerParameterIndexes.IsEmpty) - { - // No arguments, fall back to the standard conversion steps. - return CreateConversion( - unconvertedString.Syntax, - unconvertedString, - interpolatedStringConversion, - isCast: false, - conversionGroupOpt: null, - handlerType, - diagnostics); - } - - Debug.Assert(handlerParameterIndexes.All((index, paramLength) => index >= BoundInterpolatedStringArgumentPlaceholder.InstanceParameter && index < paramLength, - parameters.Length)); - - // We need to find the appropriate argument expression for every expected parameter, and error on any that occur after the current parameter - - ImmutableArray handlerArgumentIndexes; - - if (memberAnalysisResult.ArgsToParamsOpt.IsDefault && arguments.Count == parameters.Length) - { - // No parameters are missing and no remapped indexes, we can just use the original indexes - handlerArgumentIndexes = handlerParameterIndexes; - } - else - { - // Args and parameters were reordered via named parameters, or parameters are missing. Find the correct argument index for each parameter. - var handlerArgumentIndexesBuilder = ArrayBuilder.GetInstance(handlerParameterIndexes.Length, fillWithValue: BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); - for (int handlerParameterIndex = 0; handlerParameterIndex < handlerParameterIndexes.Length; handlerParameterIndex++) - { - int handlerParameter = handlerParameterIndexes[handlerParameterIndex]; - Debug.Assert(handlerArgumentIndexesBuilder[handlerParameterIndex] is BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); - - if (handlerParameter == BoundInterpolatedStringArgumentPlaceholder.InstanceParameter) - { - handlerArgumentIndexesBuilder[handlerParameterIndex] = handlerParameter; - continue; - } - - for (int argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) - { - // The index in the original parameter list we're looking to match up. - int argumentParameterIndex = memberAnalysisResult.ParameterFromArgument(argumentIndex); - // Is the original parameter index of the current argument the parameter index that was specified in the attribute? - if (argumentParameterIndex == handlerParameter) - { - // We can't just bail out on the first match: users can duplicate parameters in attributes, causing the same value to be passed twice. - handlerArgumentIndexesBuilder[handlerParameterIndex] = argumentIndex; - } - } - } - - handlerArgumentIndexes = handlerArgumentIndexesBuilder.ToImmutableAndFree(); - } - - var argumentPlaceholdersBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); - var argumentRefKindsBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); - bool hasErrors = false; - - // Now, go through all the specified arguments and see if any were specified _after_ the interpolated string, and construct - // a set of placeholders for overload resolution. - for (int i = 0; i < handlerArgumentIndexes.Length; i++) - { - int argumentIndex = handlerArgumentIndexes[i]; - Debug.Assert(argumentIndex != interpolatedStringArgNum); - - RefKind refKind; - TypeSymbol placeholderType; - switch (argumentIndex) - { - case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: - Debug.Assert(receiver!.Type is not null); - refKind = RefKind.None; - placeholderType = receiver.Type; - break; - case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: - { - // Don't error if the parameter isn't optional or params: the user will already have an error for missing an optional parameter or overload resolution failed. - // If it is optional, then they could otherwise not specify the parameter and that's an error - var originalParameterIndex = handlerParameterIndexes[i]; - var parameter = parameters[originalParameterIndex]; - if (parameter.IsOptional || - (memberAnalysisResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && originalParameterIndex + 1 == parameters.Length)) - { - // Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. - diagnostics.Add( - ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, - unconvertedString.Syntax.Location, - parameter.Name, - correspondingParameter.Name); - hasErrors = true; - } - - refKind = parameter.RefKind; - placeholderType = parameter.Type; - } - break; - default: - { - var originalParameterIndex = handlerParameterIndexes[i]; - var parameter = parameters[originalParameterIndex]; - if (argumentIndex > interpolatedStringArgNum) - { - // Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. - diagnostics.Add( - ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, - arguments[argumentIndex].Syntax.Location, - parameter.Name, - correspondingParameter.Name); - hasErrors = true; - } - - refKind = parameter.RefKind; - placeholderType = parameter.Type; - } - break; - } - - SyntaxNode placeholderSyntax; - bool isSuppressed; - - switch (argumentIndex) - { - case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: - Debug.Assert(receiver != null); - isSuppressed = receiver.IsSuppressed; - placeholderSyntax = receiver.Syntax; - break; - case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: - placeholderSyntax = unconvertedString.Syntax; - isSuppressed = false; - break; - case >= 0: - placeholderSyntax = arguments[argumentIndex].Syntax; - isSuppressed = arguments[argumentIndex].IsSuppressed; - break; - default: - throw ExceptionUtilities.UnexpectedValue(argumentIndex); - } - - argumentPlaceholdersBuilder.Add( - (BoundInterpolatedStringArgumentPlaceholder)(new BoundInterpolatedStringArgumentPlaceholder( - placeholderSyntax, - argumentIndex, - placeholderType, - hasErrors: argumentIndex == BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter) - { WasCompilerGenerated = true }.WithSuppression(isSuppressed))); - // We use the parameter refkind, rather than what the argument was actually passed with, because that will suppress duplicated errors - // about arguments being passed with the wrong RefKind. The user will have already gotten an error about mismatched RefKinds or it will - // be a place where refkinds are allowed to differ - argumentRefKindsBuilder.Add(refKind == RefKind.RefReadOnlyParameter ? RefKind.In : refKind); - } - - var interpolatedString = BindUnconvertedInterpolatedExpressionToHandlerType( - unconvertedString, - (NamedTypeSymbol)handlerType, - diagnostics, - additionalConstructorArguments: argumentPlaceholdersBuilder.ToImmutableAndFree(), - additionalConstructorRefKinds: argumentRefKindsBuilder.ToImmutableAndFree()); - - return new BoundConversion( - interpolatedString.Syntax, - interpolatedString, - interpolatedStringConversion, - @checked: CheckOverflowAtRuntime, - explicitCastInCode: false, - conversionGroupOpt: null, - constantValueOpt: null, - handlerType, - hasErrors || interpolatedString.HasErrors); - } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 5314be7a5b914..0f76849877b03 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -389,14 +389,6 @@ private BoundExpression BindDynamicInvocation( BindingDiagnosticBag diagnostics, CSharpSyntaxNode queryClause) { - // - // !!! ATTENTION !!! - // - // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check - // this function should be kept in sync with local function - // HasCollectionExpressionApplicableAddMethod.bindDynamicInvocation - // - CheckNamedArgumentsForDynamicInvocation(arguments, diagnostics); bool hasErrors = false; @@ -915,6 +907,14 @@ private bool CanEarlyBindSingleCandidateInvocationWithDynamicArgument( MemberResolutionResult methodResolutionResult, MethodSymbol singleCandidate) { + // + // !!! ATTENTION !!! + // + // In terms of errors relevant for HasCollectionExpressionApplicableAddMethod check + // this function should be kept in sync with local function + // HasCollectionExpressionApplicableAddMethod.canEarlyBindSingleCandidateInvocationWithDynamicArgument + // + if (boundMethodGroup.TypeArgumentsOpt.IsDefaultOrEmpty && singleCandidate.IsGenericMethod) { // If we call an unconstructed generic function with a @@ -1245,12 +1245,12 @@ private BoundCall BindInvocationExpressionContinued( var receiver = ReplaceTypeOrValueReceiver(methodGroup.Receiver, !method.RequiresInstanceReceiver && !invokedAsExtensionMethod, diagnostics); - this.CheckAndCoerceArguments(methodResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: invokedAsExtensionMethod); + ImmutableArray argsToParams; + this.CheckAndCoerceArguments(node, methodResult, analyzedArguments, diagnostics, receiver, invokedAsExtensionMethod: invokedAsExtensionMethod, out argsToParams); var expanded = methodResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParams = methodResult.Result.ArgsToParamsOpt; - BindDefaultArgumentsAndParamsCollection(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); + BindDefaultArguments(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, analyzedArguments.Names, ref argsToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); // Note: we specifically want to do final validation (7.6.5.1) without checking delegate compatibility (15.2), // so we're calling MethodGroupFinalValidation directly, rather than via MethodGroupConversionHasErrors. @@ -1511,7 +1511,7 @@ private BoundExpression GetDefaultParameterSpecialNoConversion(SyntaxNode syntax return parameter; } - internal void BindDefaultArgumentsAndParamsCollection( + internal void BindDefaultArguments( SyntaxNode node, ImmutableArray parameters, ArrayBuilder argumentsBuilder, @@ -1524,12 +1524,7 @@ internal void BindDefaultArgumentsAndParamsCollection( BindingDiagnosticBag diagnostics, Symbol? attributedMember = null) { - int firstParamsArgument = -1; int paramsIndex = parameters.Length - 1; - var paramsArgsBuilder = expanded ? ArrayBuilder.GetInstance() : null; - - Debug.Assert(!argumentsBuilder.Any(a => a.IsParamsArrayOrCollection)); - var visitedParameters = BitVector.Create(parameters.Length); for (var i = 0; i < argumentsBuilder.Count; i++) { @@ -1540,22 +1535,10 @@ internal void BindDefaultArgumentsAndParamsCollection( if (expanded && parameter.Ordinal == paramsIndex) { - Debug.Assert(paramsArgsBuilder is not null); - Debug.Assert(paramsArgsBuilder.Count == 0); - - firstParamsArgument = i; - paramsArgsBuilder.Add(argumentsBuilder[i]); - - for (int remainingArgument = i + 1; remainingArgument < argumentsBuilder.Count; ++remainingArgument) - { - if (GetCorrespondingParameter(remainingArgument, parameters, argsToParamsOpt, expanded: true)?.Ordinal != paramsIndex) - { - break; - } - - paramsArgsBuilder.Add(argumentsBuilder[remainingArgument]); - i++; - } + expanded = false; // For the reminder of the method treat this as non-expanded case + Debug.Assert(argumentsBuilder[i].IsParamsArrayOrCollection); + Debug.Assert(i + 1 == argumentsBuilder.Count || + GetCorrespondingParameter(i + 1, parameters, argsToParamsOpt, expanded: true)?.Ordinal != paramsIndex); } } } @@ -1574,7 +1557,6 @@ internal void BindDefaultArgumentsAndParamsCollection( Debug.Assert(argumentRefKindsBuilder is null || argumentRefKindsBuilder.Count == 0 || argumentRefKindsBuilder.Count == argumentsBuilder.Count); Debug.Assert(namesBuilder is null || namesBuilder.Count == 0 || namesBuilder.Count == argumentsBuilder.Count); Debug.Assert(argsToParamsOpt.IsDefault || argsToParamsOpt.Length == argumentsBuilder.Count); - Debug.Assert(paramsArgsBuilder is null); defaultArguments = default; return; } @@ -1586,146 +1568,6 @@ internal void BindDefaultArgumentsAndParamsCollection( argsToParamsBuilder.AddRange(argsToParamsOpt); } - BoundExpression? collection = null; - - if (expanded) - { - Debug.Assert(paramsArgsBuilder is not null); - ImmutableArray collectionArgs = paramsArgsBuilder.ToImmutableAndFree(); - int collectionArgsLength = collectionArgs.Length; - - TypeSymbol collectionType = parameters[paramsIndex].Type; - - if (collectionType is ArrayTypeSymbol { IsSZArray: true }) - { - TypeSymbol int32Type = GetSpecialType(SpecialType.System_Int32, diagnostics, node); - BoundExpression arraySize = new BoundLiteral(node, ConstantValue.Create(collectionArgsLength), int32Type) { WasCompilerGenerated = true }; - - collection = new BoundArrayCreation( - node, - ImmutableArray.Create(arraySize), - new BoundArrayInitialization(node, isInferred: false, collectionArgs) { WasCompilerGenerated = true }, - collectionType) - { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - } - else - { - if (Compilation.SourceModule != parameters[paramsIndex].ContainingModule) - { - MessageID.IDS_FeatureParamsCollections.CheckFeatureAvailability(diagnostics, node); - } - - var unconvertedCollection = new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(collectionArgs)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - Conversion conversion = Conversions.ClassifyImplicitConversionFromExpression(unconvertedCollection, collectionType, ref useSiteInfo); - diagnostics.Add(node, useSiteInfo); - - BoundCollectionExpression converted; - if (!conversion.Exists) - { - Debug.Assert(false); // Add test if this code path is reachable - GenerateImplicitConversionErrorForCollectionExpression(unconvertedCollection, collectionType, diagnostics); - converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); - } - else - { - Debug.Assert(conversion.IsCollectionExpression); - - bool infiniteRecursion = false; - if (conversion.GetCollectionExpressionTypeKind(out _, out MethodSymbol? constructor, out bool isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && - isExpanded) - { - Debug.Assert(constructor is not null); - - // Check for infinite recursion through the constructor - var constructorSet = PooledHashSet.GetInstance(); - constructorSet.Add(constructor.OriginalDefinition); - - BoundUnconvertedCollectionExpression? emptyCollection = null; - - while (true) - { - var paramsType = constructor.Parameters[^1].Type; - if (!paramsType.IsSZArray()) - { - var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; - emptyCollection ??= new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(ImmutableArray.Empty)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - Conversion nextConversion = Conversions.ClassifyImplicitConversionFromExpression(emptyCollection, paramsType, ref discardedUseSiteInfo); - - if (nextConversion.Exists && - nextConversion.GetCollectionExpressionTypeKind(out _, out constructor, out isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && - isExpanded) - { - Debug.Assert(constructor is not null); - - if (constructorSet.Add(constructor.OriginalDefinition)) - { - continue; - } - - infiniteRecursion = true; - } - } - - break; - } - - constructorSet.Free(); - } - - if (infiniteRecursion) - { - Debug.Assert(constructor is not null); - Error(diagnostics, ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, node, collectionType, constructor.OriginalDefinition); - converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); - } - else - { - converted = ConvertCollectionExpression(unconvertedCollection, collectionType, conversion, diagnostics); - } - } - - collection = new BoundConversion( - node, - converted, - conversion, - @checked: CheckOverflowAtRuntime, - explicitCastInCode: false, - conversionGroupOpt: null, - constantValueOpt: null, - type: collectionType) - { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; - } - - Debug.Assert(collection.IsParamsArrayOrCollection); - - if (collectionArgsLength != 0) - { - Debug.Assert(firstParamsArgument != -1); - Debug.Assert(!haveDefaultArguments || collectionArgsLength == 1); - Debug.Assert(collectionArgsLength == 1 || firstParamsArgument + collectionArgsLength == argumentsBuilder.Count); - - for (var i = firstParamsArgument + collectionArgsLength - 1; i != firstParamsArgument; i--) - { - argumentsBuilder.RemoveAt(i); - argsToParamsBuilder?.RemoveAt(i); - - if (argumentRefKindsBuilder is { Count: > 0 }) - { - argumentRefKindsBuilder.RemoveAt(i); - } - - if (namesBuilder is { Count: > 0 }) - { - namesBuilder.RemoveAt(i); - } - } - - argumentsBuilder[firstParamsArgument] = collection; - collection = null; - } - } - // only proceed with binding default arguments if we know there is some parameter that has not been matched by an explicit argument if (haveDefaultArguments) { @@ -1774,11 +1616,10 @@ internal void BindDefaultArgumentsAndParamsCollection( defaultArguments = default; } - if (collection is not null) + if (expanded) { - Debug.Assert(expanded); - Debug.Assert(firstParamsArgument == -1); - + // Create an empty collection + BoundExpression collection = CreateParamsCollection(node, parameters[paramsIndex], collectionArgs: ImmutableArray.Empty, diagnostics); argumentsBuilder.Add(collection); argsToParamsBuilder?.Add(paramsIndex); @@ -1924,6 +1765,116 @@ static int getArgumentIndex(int parameterIndex, ImmutableArray argsToParams } + private BoundExpression CreateParamsCollection(SyntaxNode node, ParameterSymbol paramsParameter, ImmutableArray collectionArgs, BindingDiagnosticBag diagnostics) + { + TypeSymbol collectionType = paramsParameter.Type; + BoundExpression collection; + + if (collectionType is ArrayTypeSymbol { IsSZArray: true }) + { + TypeSymbol int32Type = GetSpecialType(SpecialType.System_Int32, diagnostics, node); + BoundExpression arraySize = new BoundLiteral(node, ConstantValue.Create(collectionArgs.Length), int32Type) { WasCompilerGenerated = true }; + + collection = new BoundArrayCreation( + node, + ImmutableArray.Create(arraySize), + new BoundArrayInitialization(node, isInferred: false, collectionArgs) { WasCompilerGenerated = true }, + collectionType) + { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + } + else + { + if (Compilation.SourceModule != paramsParameter.ContainingModule) + { + MessageID.IDS_FeatureParamsCollections.CheckFeatureAvailability(diagnostics, node); + } + + var unconvertedCollection = new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(collectionArgs)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + Conversion conversion = Conversions.ClassifyImplicitConversionFromExpression(unconvertedCollection, collectionType, ref useSiteInfo); + diagnostics.Add(node, useSiteInfo); + + BoundCollectionExpression converted; + if (!conversion.Exists) + { + Debug.Assert(false); // Add test if this code path is reachable + GenerateImplicitConversionErrorForCollectionExpression(unconvertedCollection, collectionType, diagnostics); + converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); + } + else + { + Debug.Assert(conversion.IsCollectionExpression); + + bool infiniteRecursion = false; + if (conversion.GetCollectionExpressionTypeKind(out _, out MethodSymbol? constructor, out bool isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && + isExpanded) + { + Debug.Assert(constructor is not null); + + // Check for infinite recursion through the constructor + var constructorSet = PooledHashSet.GetInstance(); + constructorSet.Add(constructor.OriginalDefinition); + + BoundUnconvertedCollectionExpression? emptyCollection = null; + + while (true) + { + var paramsType = constructor.Parameters[^1].Type; + if (!paramsType.IsSZArray()) + { + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + emptyCollection ??= new BoundUnconvertedCollectionExpression(node, ImmutableArray.CastUp(ImmutableArray.Empty)) { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + Conversion nextConversion = Conversions.ClassifyImplicitConversionFromExpression(emptyCollection, paramsType, ref discardedUseSiteInfo); + + if (nextConversion.Exists && + nextConversion.GetCollectionExpressionTypeKind(out _, out constructor, out isExpanded) == CollectionExpressionTypeKind.ImplementsIEnumerable && + isExpanded) + { + Debug.Assert(constructor is not null); + + if (constructorSet.Add(constructor.OriginalDefinition)) + { + continue; + } + + infiniteRecursion = true; + } + } + + break; + } + + constructorSet.Free(); + } + + if (infiniteRecursion) + { + Debug.Assert(constructor is not null); + Error(diagnostics, ErrorCode.ERR_ParamsCollectionInfiniteChainOfConstructorCalls, node, collectionType, constructor.OriginalDefinition); + converted = BindCollectionExpressionForErrorRecovery(unconvertedCollection, collectionType, inConversion: true, diagnostics); + } + else + { + converted = ConvertCollectionExpression(unconvertedCollection, collectionType, conversion, diagnostics); + } + } + + collection = new BoundConversion( + node, + converted, + conversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: false, + conversionGroupOpt: null, + constantValueOpt: null, + type: collectionType) + { WasCompilerGenerated = true, IsParamsArrayOrCollection = true }; + } + + Debug.Assert(collection.IsParamsArrayOrCollection); + return collection; + } + #nullable disable /// @@ -2540,7 +2491,7 @@ private BoundFunctionPointerInvocation BindFunctionPointerInvocation(SyntaxNode methodsBuilder.Free(); MemberResolutionResult methodResult = overloadResolutionResult.ValidResult; - CheckAndCoerceArguments(methodResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false); + CheckAndCoerceArguments(node, methodResult, analyzedArguments, diagnostics, receiver: null, invokedAsExtensionMethod: false, argsToParamsOpt: out _); var args = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 791e5cc5027cc..c35c4512565cd 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -1654,9 +1654,14 @@ internal static NamedTypeSymbol GetSpecialType(CSharpCompilation compilation, Sp /// member isn't found. /// internal Symbol GetSpecialTypeMember(SpecialMember member, BindingDiagnosticBag diagnostics, SyntaxNode syntax) + { + return GetSpecialTypeMember(this.Compilation, member, diagnostics, syntax); + } + + internal static Symbol GetSpecialTypeMember(CSharpCompilation compilation, SpecialMember member, BindingDiagnosticBag diagnostics, SyntaxNode syntax) { Symbol memberSymbol; - return TryGetSpecialTypeMember(this.Compilation, member, syntax, diagnostics, out memberSymbol) + return TryGetSpecialTypeMember(compilation, member, syntax, diagnostics, out memberSymbol) ? memberSymbol : null; } diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 0bc9e18eb7df9..c2db44e853310 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -1205,7 +1205,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat var argsBuilder = ArrayBuilder.GetInstance(patternDisposeMethod.ParameterCount); var argsToParams = default(ImmutableArray); - BindDefaultArgumentsAndParamsCollection( + BindDefaultArguments( syntax, patternDisposeMethod.Parameters, argsBuilder, @@ -1429,9 +1429,10 @@ private MethodArgumentInfo PerformForEachPatternOverloadResolution(SyntaxNode sy } else { + Debug.Assert(analyzedArguments.Arguments.Count == 0); var argsToParams = overloadResolutionResult.ValidResult.Result.ArgsToParamsOpt; var expanded = overloadResolutionResult.ValidResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - BindDefaultArgumentsAndParamsCollection( + BindDefaultArguments( syntax, result.Parameters, analyzedArguments.Arguments, @@ -1883,7 +1884,7 @@ private MethodArgumentInfo BindDefaultArguments(MethodSymbol method, BoundExpres } ImmutableArray argsToParams = default; - BindDefaultArgumentsAndParamsCollection( + BindDefaultArguments( syntax, method.Parameters, argsBuilder, diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 6d6e983a1ef12..2029d8d5aa669 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -539,6 +539,13 @@ private void RemoveLocalScopes(LocalSymbol local) return null; } + public override BoundNode? VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node) + { + base.VisitCompoundAssignmentOperator(node); + ValidateAssignment(node.Syntax, node.Left, node, isRef: false, _diagnostics); + return null; + } + public override BoundNode? VisitIsPatternExpression(BoundIsPatternExpression node) { this.Visit(node.Expression); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 3332390061a99..abb608ffe3b20 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -194,7 +194,7 @@ protected override Conversion GetCollectionExpressionConversion( } if (elements.Length > 0 && - !_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, elementType, addMethods: out _, BindingDiagnosticBag.Discarded)) + !_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, addMethods: out _, BindingDiagnosticBag.Discarded)) { return Conversion.NoConversion; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index 30580a2a6e42f..f0a6c46065c3a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -14,8 +14,79 @@ namespace Microsoft.CodeAnalysis.CSharp { [SuppressMessage("Performance", "CA1067", Justification = "Equality not actually implemented")] - internal readonly struct MemberAnalysisResult + internal +#if !DEBUG + readonly +#endif + struct MemberAnalysisResult { +#if DEBUG + private readonly ImmutableArray _conversionsOpt; + public ImmutableArray ConversionsOpt + { + get + { + Debug.Assert(!_argumentsCoerced); + return _conversionsOpt; + } + private init + { + _conversionsOpt = value; + } + } + + /// + /// A bit vector representing whose true bits indicate indices of bad arguments + /// + /// + /// The capacity of this BitVector might not match the parameter count of the method overload being resolved. + /// For example, if a method overload has 5 parameters and the second parameter is the only bad parameter, then this + /// BitVector could end up with Capacity being 2 where BadArguments[0] is false and BadArguments[1] is true. + /// + private readonly BitVector _badArgumentsOpt; + public BitVector BadArgumentsOpt + { + get + { + Debug.Assert(!_argumentsCoerced); + return _badArgumentsOpt; + } + private init + { + _badArgumentsOpt = value; + } + } + + private readonly ImmutableArray _argsToParamsOpt; + public ImmutableArray ArgsToParamsOpt + { + get + { + Debug.Assert(!_argumentsCoerced); + return _argsToParamsOpt; + } + private init + { + _argsToParamsOpt = value; + } + } + + private readonly ImmutableArray _constraintFailureDiagnostics; + public ImmutableArray ConstraintFailureDiagnostics + { + get + { + Debug.Assert(!_argumentsCoerced); + return _constraintFailureDiagnostics; + } + private init + { + _constraintFailureDiagnostics = value; + } + } + + private bool _argumentsCoerced; +#else // put these first for better packing public readonly ImmutableArray ConversionsOpt; @@ -30,6 +101,7 @@ internal readonly struct MemberAnalysisResult public readonly BitVector BadArgumentsOpt; public readonly ImmutableArray ArgsToParamsOpt; public readonly ImmutableArray ConstraintFailureDiagnostics; +#endif public readonly int BadParameter; public readonly MemberResolutionKind Kind; @@ -325,5 +397,13 @@ internal static MemberAnalysisResult WrongCallingConvention() { return new MemberAnalysisResult(MemberResolutionKind.WrongCallingConvention); } + + [Conditional("DEBUG")] + public void ArgumentsWereCoerced() + { +#if DEBUG + _argumentsCoerced = true; +#endif + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index d5b9a3efeb4ae..d70eed323fcdc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -126,6 +126,7 @@ public enum Options : byte IsFunctionPointerResolution = 0b_00010000, IsExtensionMethodResolution = 0b_00100000, DynamicResolution = 0b_01000000, + DynamicConvertsToAnything = 0b_10000000, } // Perform overload resolution on the given method group, with the given arguments and @@ -870,6 +871,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( hasAnyRefOmittedArgument: false, ignoreOpenTypes: false, completeResults: completeResults, + dynamicConvertsToAnything: false, useSiteInfo: ref useSiteInfo); } @@ -911,6 +913,7 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm( hasAnyRefOmittedArgument: false, ignoreOpenTypes: false, completeResults: completeResults, + dynamicConvertsToAnything: false, useSiteInfo: ref useSiteInfo); Debug.Assert(!result.IsValid || result.Kind == MemberResolutionKind.ApplicableInExpandedForm); @@ -1063,6 +1066,7 @@ private void AddMemberToCandidateSet( arguments, allowRefOmittedArguments: (options & Options.AllowRefOmittedArguments) != 0, completeResults: completeResults, + dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, useSiteInfo: ref useSiteInfo); if (PreferExpandedFormOverNormalForm(normalResult, expandedResult)) @@ -1210,7 +1214,7 @@ public static bool TryInferParamsCollectionIterationType(Binder binder, TypeSymb return false; } - if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, type, elementType.Type, addMethods: out _, BindingDiagnosticBag.Discarded)) + if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, type, addMethods: out _, BindingDiagnosticBag.Discarded)) { return false; } @@ -2332,13 +2336,16 @@ private BetterResult BetterFunctionMember( TypeSymbol t1 = m1LeastOverriddenParameters[^1].Type; TypeSymbol t2 = m2LeastOverriddenParameters[^1].Type; - if (IsBetterParamsCollectionType(t1, t2, ref useSiteInfo)) + if (!Conversions.HasIdentityConversion(t1, t2)) { - return BetterResult.Left; - } - if (IsBetterParamsCollectionType(t2, t1, ref useSiteInfo)) - { - return BetterResult.Right; + if (IsBetterParamsCollectionType(t1, t2, ref useSiteInfo)) + { + return BetterResult.Left; + } + if (IsBetterParamsCollectionType(t2, t1, ref useSiteInfo)) + { + return BetterResult.Right; + } } } } @@ -2778,6 +2785,7 @@ private bool IsBetterCollectionExpressionConversion( TypeSymbol t2, CollectionExpressionTypeKind kind2, TypeSymbol elementType2, ref CompoundUseSiteInfo useSiteInfo) { + Debug.Assert(!Conversions.HasIdentityConversion(t1, t2)); // - T1 is System.ReadOnlySpan, and T2 is System.Span, and an implicit conversion exists from E1 to E2 if (kind1 is CollectionExpressionTypeKind.ReadOnlySpan && @@ -3702,6 +3710,7 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, inferWithDynamic: (options & Options.InferWithDynamic) != 0, completeResults: completeResults, + dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, useSiteInfo: ref useSiteInfo); // If we were producing complete results and had missing arguments, we pushed on in order to call IsApplicable for @@ -3721,6 +3730,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm useSiteInfo) where TMember : Symbol { @@ -3763,6 +3773,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm IsApplicable( bool hasAnyRefOmittedArgument, bool inferWithDynamic, bool completeResults, + bool dynamicConvertsToAnything, ref CompoundUseSiteInfo useSiteInfo) where TMember : Symbol { @@ -3898,6 +3910,7 @@ private MemberResolutionResult IsApplicable( hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, ignoreOpenTypes: ignoreOpenTypes, completeResults: completeResults, + dynamicConvertsToAnything: dynamicConvertsToAnything, useSiteInfo: ref useSiteInfo); return new MemberResolutionResult(member, leastOverriddenMember, applicableResult, hasTypeArgumentsInferredFromFunctionType); } @@ -3967,6 +3980,7 @@ private MemberAnalysisResult IsApplicable( bool hasAnyRefOmittedArgument, bool ignoreOpenTypes, bool completeResults, + bool dynamicConvertsToAnything, ref CompoundUseSiteInfo useSiteInfo) { TypeWithAnnotations paramsElementTypeOpt; @@ -4079,9 +4093,15 @@ private MemberAnalysisResult IsApplicable( ignoreOpenTypes, ref useSiteInfo, forExtensionMethodThisArg, - hasInterpolatedStringRefMismatch); + hasInterpolatedStringRefMismatch, + dynamicConvertsToAnything); + + Debug.Assert( + !forExtensionMethodThisArg || + (!conversion.IsDynamic || + (ignoreOpenTypes && parameters.ParameterTypes[argumentPosition].Type.ContainsTypeParameter(parameterContainer: (MethodSymbol)candidate)))); - if (forExtensionMethodThisArg && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)) + if (forExtensionMethodThisArg && !conversion.IsDynamic && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)) { // Return early, without checking conversions of subsequent arguments, // if the instance argument is not convertible to the 'this' parameter, @@ -4150,7 +4170,8 @@ private Conversion CheckArgumentForApplicability( bool ignoreOpenTypes, ref CompoundUseSiteInfo useSiteInfo, bool forExtensionMethodThisArg, - bool hasInterpolatedStringRefMismatch) + bool hasInterpolatedStringRefMismatch, + bool dynamicConvertsToAnything) { // Spec 7.5.3.1 // For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical @@ -4191,7 +4212,9 @@ private Conversion CheckArgumentForApplicability( { var conversion = forExtensionMethodThisArg ? Conversions.ClassifyImplicitExtensionMethodThisArgConversion(argument, argument.Type, parameterType, ref useSiteInfo) : - Conversions.ClassifyImplicitConversionFromExpression(argument, parameterType, ref useSiteInfo); + ((!dynamicConvertsToAnything || !argument.Type.IsDynamic()) ? + Conversions.ClassifyImplicitConversionFromExpression(argument, parameterType, ref useSiteInfo) : + Conversion.ImplicitDynamic); Debug.Assert((!conversion.Exists) || conversion.IsImplicit, "ClassifyImplicitConversion should only return implicit conversions"); if (hasInterpolatedStringRefMismatch && !conversion.IsInterpolatedStringHandler) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs index 188d1893ffebb..45c569b13deaa 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs @@ -308,7 +308,7 @@ internal void ReportDiagnostics( // Otherwise, if there is any such method that has a bad argument conversion or out/ref mismatch // then the first such method found is the best bad method. - if (HadBadArguments(diagnostics, binder, name, arguments, symbols, location, binder.Flags, isMethodGroupConversion)) + if (HadBadArguments(diagnostics, binder, name, receiver, arguments, symbols, location, binder.Flags, isMethodGroupConversion)) { return; } @@ -519,8 +519,7 @@ internal void ReportDiagnostics( else { Debug.Assert(firstSupported.Member is MethodSymbol { Name: "Add" }); - int argumentOffset = arguments.IsExtensionMethodInvocation ? 1 : 0; - diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, arguments.Arguments[argumentOffset].Type, firstSupported.Member); + diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, receiver.Type); } } else @@ -1081,6 +1080,7 @@ private bool HadBadArguments( BindingDiagnosticBag diagnostics, Binder binder, string name, + BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray symbols, Location location, @@ -1129,8 +1129,7 @@ private bool HadBadArguments( if (flags.Includes(BinderFlags.CollectionExpressionConversionValidation)) { - Debug.Assert(arguments.Arguments.Count == argumentOffset + 1); - diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, arguments.Arguments[argumentOffset].Type, method); + diagnostics.Add(ErrorCode.ERR_CollectionExpressionMissingAdd, location, receiver.Type); } else { diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 522d191faa3db..775774fe0188c 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -207,7 +207,7 @@ bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeI var argumentsBuilder = ArrayBuilder.GetInstance(disposeMethod.ParameterCount); ImmutableArray argsToParams = default; - originalBinder.BindDefaultArgumentsAndParamsCollection( + originalBinder.BindDefaultArguments( // If this is a using statement, then we want to use the whole `using (expr) { }` as the argument location. These arguments // will be represented in the IOperation tree and the "correct" node for them, given that they are an implicit invocation // at the end of the using statement, is on the whole using statement, not on the current expression. diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 6440e5877788a..35d9967120efb 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5717,10 +5717,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Nullability of reference types in type of parameter doesn't match implemented member (possibly because of nullability attributes). - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. Nullability of reference types in value of type '{0}' doesn't match target type '{1}'. @@ -6863,7 +6863,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. The CollectionBuilderAttribute builder type must be a non-generic class or struct. diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 16a451b1d9bf5..f71459b318246 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -1629,9 +1629,9 @@ internal AliasSymbol GlobalNamespaceAlias /// /// Get the symbol for the predefined type from the COR Library referenced by this compilation. /// - internal new NamedTypeSymbol GetSpecialType(SpecialType specialType) + internal NamedTypeSymbol GetSpecialType(ExtendedSpecialType specialType) { - if (specialType <= SpecialType.None || specialType > SpecialType.Count) + if ((int)specialType <= (int)SpecialType.None || (int)specialType >= (int)InternalSpecialType.NextAvailable) { throw new ArgumentOutOfRangeException(nameof(specialType), $"Unexpected SpecialType: '{(int)specialType}'."); } @@ -1647,7 +1647,7 @@ internal AliasSymbol GlobalNamespaceAlias result = Assembly.GetSpecialType(specialType); } - Debug.Assert(result.SpecialType == specialType); + Debug.Assert(result.ExtendedSpecialType == specialType); return result; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 5f7eacbfddf06..bf4e997c62729 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6718,7 +6718,13 @@ private ImmutableArray VisitArguments( (ParameterSymbol? parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, bool isExpandedParamsArgument) = GetCorrespondingParameter(i, parametersOpt, argsToParamsOpt, expanded, ref paramsIterationType); - if (parameter is null) + if (// This is known to happen for certain error scenarios, because + // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` + // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. + parameter is null || + // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type + // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. + node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true }) { if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) { @@ -6728,10 +6734,7 @@ private ImmutableArray VisitArguments( completion(TypeWithAnnotations.Create(argument.Type)); TargetTypedAnalysisCompletion.Remove(argumentNoConversion); - // This is known to happen for certain error scenarios, because - // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` - // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. - Debug.Assert(method is ErrorMethodSymbol); + Debug.Assert(parameter is not null || method is ErrorMethodSymbol); } continue; } diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ExpressionLambdaRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ExpressionLambdaRewriter.cs index aea66806e9933..37849dc7d66ff 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ExpressionLambdaRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ExpressionLambdaRewriter.cs @@ -339,7 +339,7 @@ private BoundExpression Expressions(ImmutableArray expressions) private BoundExpression VisitArrayCreation(BoundArrayCreation node) { var arrayType = (ArrayTypeSymbol)node.Type; - var boundType = _bound.Typeof(arrayType.ElementType); + var boundType = _bound.Typeof(arrayType.ElementType, _bound.WellKnownType(WellKnownType.System_Type)); if (node.InitializerOpt != null) { if (arrayType.IsSZArray) @@ -374,7 +374,7 @@ private BoundExpression VisitAsOperator(BoundAsOperator node) node = node.Update(operand, node.TargetType, node.OperandPlaceholder, node.OperandConversion, node.Type); } - return ExprFactory("TypeAs", Visit(node.Operand), _bound.Typeof(node.Type)); + return ExprFactory("TypeAs", Visit(node.Operand), _bound.Typeof(node.Type, _bound.WellKnownType(WellKnownType.System_Type))); } private BoundExpression VisitBaseReference(BoundBaseReference node) @@ -502,8 +502,10 @@ private BoundExpression MakeBinary(MethodSymbol methodOpt, TypeSymbol type, bool { return ((object)methodOpt == null) ? ExprFactory(opName, loweredLeft, loweredRight) : - requiresLifted ? ExprFactory(opName, loweredLeft, loweredRight, _bound.Literal(isLifted && !TypeSymbol.Equals(methodOpt.ReturnType, type, TypeCompareKind.ConsiderEverything2)), _bound.MethodInfo(methodOpt)) : - ExprFactory(opName, loweredLeft, loweredRight, _bound.MethodInfo(methodOpt)); + requiresLifted ? + ExprFactory(opName, loweredLeft, loweredRight, _bound.Literal(isLifted && !TypeSymbol.Equals(methodOpt.ReturnType, type, TypeCompareKind.ConsiderEverything2)), + _bound.MethodInfo(methodOpt, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo))) : + ExprFactory(opName, loweredLeft, loweredRight, _bound.MethodInfo(methodOpt, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo))); } private TypeSymbol PromotedType(TypeSymbol underlying) @@ -577,7 +579,7 @@ private BoundExpression VisitCall(BoundCall node) return ExprFactory( "Call", method.RequiresInstanceReceiver ? Visit(node.ReceiverOpt) : _bound.Null(ExpressionType), - _bound.MethodInfo(method), + _bound.MethodInfo(method, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo)), Expressions(node.Arguments)); } } @@ -638,7 +640,10 @@ private BoundExpression VisitConversion(BoundConversion node) var e1 = requireAdditionalCast ? Convert(Visit(node.Operand), node.Operand.Type, method.Parameters[0].Type, node.Checked, false) : Visit(node.Operand); - var e2 = ExprFactory(node.Checked && SyntaxFacts.IsCheckedOperator(method.Name) ? "ConvertChecked" : "Convert", e1, _bound.Typeof(resultType), _bound.MethodInfo(method)); + var e2 = ExprFactory(node.Checked && SyntaxFacts.IsCheckedOperator(method.Name) ? + "ConvertChecked" : + "Convert", e1, _bound.Typeof(resultType, _bound.WellKnownType(WellKnownType.System_Type)), + _bound.MethodInfo(method, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo))); return Convert(e2, resultType, node.Type, node.Checked, false); } case ConversionKind.ImplicitReference: @@ -674,7 +679,7 @@ private BoundExpression Convert(BoundExpression operand, TypeSymbol oldType, Typ private BoundExpression Convert(BoundExpression expr, TypeSymbol type, bool isChecked) { - return ExprFactory(isChecked ? "ConvertChecked" : "Convert", expr, _bound.Typeof(type)); + return ExprFactory(isChecked ? "ConvertChecked" : "Convert", expr, _bound.Typeof(type, _bound.WellKnownType(WellKnownType.System_Type))); } private BoundExpression DelegateCreation(BoundExpression receiver, MethodSymbol method, TypeSymbol delegateType, bool requiresInstanceReceiver) @@ -687,14 +692,16 @@ private BoundExpression DelegateCreation(BoundExpression receiver, MethodSymbol if ((object)createDelegate != null) { // beginning in 4.5, we do it this way - unquoted = _bound.Call(_bound.MethodInfo(method), createDelegate, _bound.Typeof(delegateType), receiver); + unquoted = _bound.Call(_bound.MethodInfo(method, createDelegate.ContainingType), createDelegate, _bound.Typeof(delegateType, createDelegate.Parameters[0].Type), receiver); } else { // 4.0 and earlier we do it this way - //createDelegate = (MethodSymbol)Bound.WellKnownMember(WellKnownMember.System_Delegate__CreateDelegate); - //operand = Bound.Call(nullObject, createDelegate, Bound.Typeof(node.Type), receiver, Bound.MethodInfo(method)); - unquoted = _bound.StaticCall(_bound.SpecialType(SpecialType.System_Delegate), "CreateDelegate", _bound.Typeof(delegateType), receiver, _bound.MethodInfo(method)); + createDelegate = _bound.SpecialMethod(SpecialMember.System_Delegate__CreateDelegate); + unquoted = _bound.Call(null, createDelegate, + _bound.Typeof(delegateType, createDelegate.Parameters[0].Type), + receiver, + _bound.MethodInfo(method, createDelegate.Parameters[2].Type)); } // NOTE: we visit the just-built node, which has not yet been visited. This is not the usual order @@ -742,7 +749,7 @@ private BoundExpression VisitIsOperator(BoundIsOperator node) operand = _bound.Null(_objectType); } - return ExprFactory("TypeIs", Visit(operand), _bound.Typeof(node.TargetType.Type)); + return ExprFactory("TypeIs", Visit(operand), _bound.Typeof(node.TargetType.Type, _bound.WellKnownType(WellKnownType.System_Type))); } private BoundExpression VisitLambda(BoundLambda node) @@ -765,7 +772,7 @@ private BoundExpression VisitLambdaInternal(BoundLambda node) parameters.Add(parameterReference); var parameter = ExprFactory( "Parameter", - _bound.Typeof(_typeMap.SubstituteType(p.Type).Type), _bound.Literal(p.Name)); + _bound.Typeof(_typeMap.SubstituteType(p.Type).Type, _bound.WellKnownType(WellKnownType.System_Type)), _bound.Literal(p.Name)); initializers.Add(_bound.AssignmentExpression(parameterReference, parameter)); _parameterMap[p] = parameterReference; } @@ -788,7 +795,7 @@ private BoundExpression VisitLambdaInternal(BoundLambda node) private BoundExpression VisitNewT(BoundNewT node) { - return VisitObjectCreationContinued(ExprFactory("New", _bound.Typeof(node.Type)), node.InitializerExpressionOpt); + return VisitObjectCreationContinued(ExprFactory("New", _bound.Typeof(node.Type, _bound.WellKnownType(WellKnownType.System_Type))), node.InitializerExpressionOpt); } private BoundExpression VisitNullCoalescingOperator(BoundNullCoalescingOperator node) @@ -813,7 +820,7 @@ private BoundExpression MakeConversionLambda(Conversion conversion, TypeSymbol f ParameterSymbol lambdaParameter = _bound.SynthesizedParameter(fromType, parameterName); var param = _bound.SynthesizedLocal(ParameterExpressionType); var parameterReference = _bound.Local(param); - var parameter = ExprFactory("Parameter", _bound.Typeof(fromType), _bound.Literal(parameterName)); + var parameter = ExprFactory("Parameter", _bound.Typeof(fromType, _bound.WellKnownType(WellKnownType.System_Type)), _bound.Literal(parameterName)); _parameterMap[lambdaParameter] = parameterReference; var convertedValue = Visit(_bound.Convert(toType, _bound.Parameter(lambdaParameter), conversion)); _parameterMap.Remove(lambdaParameter); @@ -834,7 +841,7 @@ private BoundExpression InitializerMemberSetter(Symbol symbol) case SymbolKind.Field: return _bound.Convert(MemberInfoType, _bound.FieldInfo((FieldSymbol)symbol)); case SymbolKind.Property: - return _bound.MethodInfo(((PropertySymbol)symbol).GetOwnOrInheritedSetMethod()); + return _bound.MethodInfo(((PropertySymbol)symbol).GetOwnOrInheritedSetMethod(), _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo)); case SymbolKind.Event: return _bound.Convert(MemberInfoType, _bound.FieldInfo(((EventSymbol)symbol).AssociatedField)); default: @@ -849,7 +856,7 @@ private BoundExpression InitializerMemberGetter(Symbol symbol) case SymbolKind.Field: return _bound.Convert(MemberInfoType, _bound.FieldInfo((FieldSymbol)symbol)); case SymbolKind.Property: - return _bound.MethodInfo(((PropertySymbol)symbol).GetOwnOrInheritedGetMethod()); + return _bound.MethodInfo(((PropertySymbol)symbol).GetOwnOrInheritedGetMethod(), _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo)); case SymbolKind.Event: return _bound.Convert(MemberInfoType, _bound.FieldInfo(((EventSymbol)symbol).AssociatedField)); default: @@ -917,7 +924,7 @@ private BoundExpression VisitInitializer(BoundExpression node, out InitializerKi // Dynamic calls are not allowed in ETs, an error is reported in diagnostics pass. foreach (BoundCollectionElementInitializer i in ci.Initializers) { - BoundExpression elementInit = ExprFactory("ElementInit", _bound.MethodInfo(i.AddMethod), Expressions(i.Arguments)); + BoundExpression elementInit = ExprFactory("ElementInit", _bound.MethodInfo(i.AddMethod, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo)), Expressions(i.Arguments)); builder.Add(elementInit); } @@ -966,7 +973,7 @@ private BoundExpression VisitObjectCreationExpressionInternal(BoundObjectCreatio (node.Arguments.Length == 0 && !node.Type.IsStructType()) || node.Constructor.IsDefaultValueTypeConstructor()) { - return ExprFactory("New", _bound.Typeof(node.Type)); + return ExprFactory("New", _bound.Typeof(node.Type, _bound.WellKnownType(WellKnownType.System_Type))); } var ctor = _bound.ConstructorInfo(node.Constructor); @@ -977,7 +984,7 @@ private BoundExpression VisitObjectCreationExpressionInternal(BoundObjectCreatio var membersBuilder = ArrayBuilder.GetInstance(); for (int i = 0; i < node.Arguments.Length; i++) { - membersBuilder.Add(_bound.MethodInfo(AnonymousTypeManager.GetAnonymousTypeProperty(anonType, i).GetMethod)); + membersBuilder.Add(_bound.MethodInfo(AnonymousTypeManager.GetAnonymousTypeProperty(anonType, i).GetMethod, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo))); } return ExprFactory("New", ctor, args, _bound.ArrayOrEmpty(MemberInfoType, membersBuilder.ToImmutableAndFree())); @@ -1029,7 +1036,7 @@ private BoundExpression VisitPropertyAccess(BoundPropertyAccess node) receiver = this.Convert(receiver, getMethod.ReceiverType, isChecked: false); } - return ExprFactory("Property", receiver, _bound.MethodInfo(getMethod)); + return ExprFactory("Property", receiver, _bound.MethodInfo(getMethod, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo))); } private static BoundExpression VisitSizeOfOperator(BoundSizeOfOperator node) @@ -1080,7 +1087,7 @@ private BoundExpression VisitUnaryOperator(BoundUnaryOperator node) return ((object)node.MethodOpt == null) ? ExprFactory(opname, loweredArg) - : ExprFactory(opname, loweredArg, _bound.MethodInfo(node.MethodOpt)); + : ExprFactory(opname, loweredArg, _bound.MethodInfo(node.MethodOpt, _bound.WellKnownType(WellKnownType.System_Reflection_MethodInfo))); } // ====================================================== @@ -1100,7 +1107,7 @@ private BoundExpression Constant(BoundExpression node) return ExprFactory( "Constant", _bound.Convert(_objectType, node), - _bound.Typeof(node.Type)); + _bound.Typeof(node.Type, _bound.WellKnownType(WellKnownType.System_Type))); } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/LocalStateTracingInstrumenter.cs b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/LocalStateTracingInstrumenter.cs index 2cb55cea8027a..352fb6eab69f3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/Instrumentation/LocalStateTracingInstrumenter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/Instrumentation/LocalStateTracingInstrumenter.cs @@ -266,6 +266,9 @@ static bool hasOverriddenToString(TypeSymbol variableType) private MethodSymbol? GetWellKnownMethodSymbol(WellKnownMember overload, SyntaxNode syntax) => (MethodSymbol?)Binder.GetWellKnownTypeMember(_factory.Compilation, overload, _diagnostics, syntax: syntax, isOptional: false); + private MethodSymbol? GetSpecialMethodSymbol(SpecialMember overload, SyntaxNode syntax) + => (MethodSymbol?)Binder.GetSpecialTypeMember(_factory.Compilation, overload, _diagnostics, syntax: syntax); + public override void PreInstrumentBlock(BoundBlock original, LocalRewriter rewriter) { Previous.PreInstrumentBlock(original, rewriter); @@ -456,7 +459,7 @@ private ImmutableArray MakeStoreLoggerArguments( if (parameter.Type.SpecialType == SpecialType.System_String && targetType.SpecialType != SpecialType.System_String) { - var toStringMethod = GetWellKnownMethodSymbol(WellKnownMember.System_Object__ToString, value.Syntax); + var toStringMethod = GetSpecialMethodSymbol(SpecialMember.System_Object__ToString, value.Syntax); BoundExpression toString; if (toStringMethod is null) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index b97db610386a9..30707d0245d8f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -626,7 +626,7 @@ private static MethodSymbol UnsafeGetSpecialTypeMethod(SyntaxNode syntax, Specia else { MemberDescriptor descriptor = SpecialMembers.GetDescriptor(specialMember); - SpecialType type = (SpecialType)descriptor.DeclaringTypeId; + ExtendedSpecialType type = descriptor.DeclaringSpecialType; TypeSymbol container = compilation.Assembly.GetSpecialType(type); TypeSymbol returnType = new ExtendedErrorTypeSymbol(compilation: compilation, name: descriptor.Name, errorInfo: null, arity: descriptor.Arity); return new ErrorMethodSymbol(container, returnType, "Missing"); @@ -645,6 +645,8 @@ private static bool TryGetSpecialTypeMethod(SyntaxNode syntax, SpecialMember spe public override BoundNode VisitTypeOfOperator(BoundTypeOfOperator node) { + Debug.Assert(node.Type.ExtendedSpecialType == InternalSpecialType.System_Type || + TypeSymbol.Equals(node.Type, _compilation.GetWellKnownType(WellKnownType.System_Type), TypeCompareKind.AllIgnoreOptions)); Debug.Assert(node.GetTypeFromHandle is null); var sourceType = (BoundTypeExpression?)this.Visit(node.SourceType); @@ -653,11 +655,24 @@ public override BoundNode VisitTypeOfOperator(BoundTypeOfOperator node) // Emit needs this helper MethodSymbol? getTypeFromHandle; - if (!TryGetWellKnownTypeMember(node.Syntax, WellKnownMember.System_Type__GetTypeFromHandle, out getTypeFromHandle)) + bool tryGetResult; + + if (node.Type.ExtendedSpecialType == InternalSpecialType.System_Type) + { + tryGetResult = TryGetSpecialTypeMethod(node.Syntax, SpecialMember.System_Type__GetTypeFromHandle, out getTypeFromHandle); + } + else + { + tryGetResult = TryGetWellKnownTypeMember(node.Syntax, WellKnownMember.System_Type__GetTypeFromHandle, out getTypeFromHandle); + } + + if (!tryGetResult) { return new BoundTypeOfOperator(node.Syntax, sourceType, null, type, hasErrors: true); } + Debug.Assert(getTypeFromHandle is not null); + Debug.Assert(TypeSymbol.Equals(type, getTypeFromHandle.ReturnType, TypeCompareKind.AllIgnoreOptions)); return node.Update(sourceType, getTypeFromHandle, type); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 0b93498251cd9..7e7c8d253ac70 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -1404,7 +1404,7 @@ private BoundExpression CreateEmptyArray(SyntaxNode syntax, ArrayTypeSymbol arra return null; } - MethodSymbol? arrayEmpty = _compilation.GetWellKnownTypeMember(WellKnownMember.System_Array__Empty) as MethodSymbol; + MethodSymbol? arrayEmpty = _compilation.GetSpecialTypeMember(SpecialMember.System_Array__Empty) as MethodSymbol; if (arrayEmpty is null) // will be null if Array.Empty doesn't exist in reference assemblies { return null; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index ca0c4e3354ab1..ef6e145345c23 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -118,7 +118,7 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele if (element is BoundCollectionExpressionSpreadElement spreadElement) { Debug.Assert(spreadElement.IteratorBody is { }); - var iteratorBody = Binder.GetUnderlyingCollectionExpressionElement(node, ((BoundExpressionStatement)spreadElement.IteratorBody).Expression); + var iteratorBody = Binder.GetUnderlyingCollectionExpressionElement(node, ((BoundExpressionStatement)spreadElement.IteratorBody).Expression, throwOnErrors: true); Debug.Assert(iteratorBody is { }); return spreadElement.Update( spreadElement.Expression, @@ -131,7 +131,7 @@ static BoundNode unwrapListElement(BoundCollectionExpression node, BoundNode ele } else { - var result = Binder.GetUnderlyingCollectionExpressionElement(node, (BoundExpression)element); + var result = Binder.GetUnderlyingCollectionExpressionElement(node, (BoundExpression)element, throwOnErrors: true); Debug.Assert(result is { }); return result; } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs index 0a9644a25fe6b..a3d1d9eb26498 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs @@ -308,7 +308,7 @@ private BoundExpression RewriteNoPiaEventAssignmentOperator(BoundEventAssignment if ((object)addRemove != null) { - BoundExpression eventInfo = _factory.New(ctor, _factory.Typeof(node.Event.ContainingType), _factory.Literal(node.Event.MetadataName)); + BoundExpression eventInfo = _factory.New(ctor, _factory.Typeof(node.Event.ContainingType, ctor.Parameters[0].Type), _factory.Literal(node.Event.MetadataName)); result = _factory.Call(eventInfo, addRemove, _factory.Convert(addRemove.Parameters[0].Type, rewrittenReceiver), _factory.Convert(addRemove.Parameters[1].Type, rewrittenArgument)); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs index 669f89e230748..892a1ba24ef29 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FixedStatement.cs @@ -535,7 +535,7 @@ private BoundStatement InitializeFixedStatementArrayLocal( else { MethodSymbol? lengthMethod; - if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Array__get_Length, out lengthMethod)) + if (TryGetSpecialTypeMethod(fixedInitializer.Syntax, SpecialMember.System_Array__get_Length, out lengthMethod)) { lengthCall = factory.Call(factory.Local(pinnedTemp), lengthMethod); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs index dd1ca01c49b65..0a264ac61f155 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs @@ -102,7 +102,7 @@ internal LoweredDynamicOperation MakeDynamicConversion( _factory.Literal((int)binderFlags), // target type: - _factory.Typeof(resultType), + _factory.Typeof(resultType, _factory.WellKnownType(WellKnownType.System_Type)), // context: _factory.TypeofDynamicOperationContextType() @@ -224,7 +224,7 @@ internal LoweredDynamicOperation MakeDynamicMemberInvocation( bool receiverIsStaticType; if (loweredReceiver.Kind == BoundKind.TypeExpression) { - loweredReceiver = _factory.Typeof(((BoundTypeExpression)loweredReceiver).Type); + loweredReceiver = _factory.Typeof(((BoundTypeExpression)loweredReceiver).Type, _factory.WellKnownType(WellKnownType.System_Type)); receiverRefKind = RefKind.None; receiverIsStaticType = true; } @@ -246,7 +246,7 @@ internal LoweredDynamicOperation MakeDynamicMemberInvocation( // type arguments: typeArgumentsWithAnnotations.IsDefaultOrEmpty ? _factory.Null(_factory.WellKnownArrayType(WellKnownType.System_Type)) : - _factory.ArrayOrEmpty(_factory.WellKnownType(WellKnownType.System_Type), _factory.TypeOfs(typeArgumentsWithAnnotations)), + _factory.ArrayOrEmpty(_factory.WellKnownType(WellKnownType.System_Type), _factory.TypeOfs(typeArgumentsWithAnnotations, _factory.WellKnownType(WellKnownType.System_Type))), // context: _factory.TypeofDynamicOperationContextType(), @@ -338,7 +338,7 @@ internal LoweredDynamicOperation MakeDynamicConstructorInvocation( { _factory.Syntax = syntax; - var loweredReceiver = _factory.Typeof(type); + var loweredReceiver = _factory.Typeof(type, _factory.WellKnownType(WellKnownType.System_Type)); MethodSymbol argumentInfoFactory = GetArgumentInfoFactory(); var binderConstruction = ((object)argumentInfoFactory != null) ? MakeBinderConstruction(WellKnownMember.Microsoft_CSharp_RuntimeBinder_Binder__InvokeConstructor, new[] diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 0dbaf36416b82..b94eddbaea342 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -397,22 +397,56 @@ public MethodSymbol WellKnownMethod(WellKnownMember wm) /// The desired special member /// A symbol for the special member. public Symbol SpecialMember(SpecialMember sm) + { + var result = SpecialMember(sm, isOptional: false); + Debug.Assert(result is not null); + return result; + } + + public Symbol? SpecialMember(SpecialMember sm, bool isOptional = false) { Symbol specialMember = Compilation.GetSpecialTypeMember(sm); if (specialMember is null) { + if (isOptional) + { + return null; + } + RuntimeMembers.MemberDescriptor memberDescriptor = SpecialMembers.GetDescriptor(sm); var diagnostic = new CSDiagnostic(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, memberDescriptor.DeclaringTypeMetadataName, memberDescriptor.Name), Syntax.Location); throw new MissingPredefinedMember(diagnostic); } - Binder.ReportUseSite(specialMember, Diagnostics, Syntax); + UseSiteInfo useSiteInfo = specialMember.GetUseSiteInfo(); + + if (isOptional) + { + if (useSiteInfo.DiagnosticInfo?.DefaultSeverity == DiagnosticSeverity.Error) + { + return null; + } + + // Not interested in warnings + } + else + { + Diagnostics.Add(useSiteInfo, Syntax); + } + return specialMember; } public MethodSymbol SpecialMethod(SpecialMember sm) { - return (MethodSymbol)SpecialMember(sm); + var result = (MethodSymbol?)SpecialMember(sm, isOptional: false); + Debug.Assert(result is not null); + return result; + } + + public MethodSymbol? SpecialMethod(SpecialMember sm, bool isOptional) + { + return (MethodSymbol?)SpecialMember(sm, isOptional); } public PropertySymbol SpecialProperty(SpecialMember sm) @@ -825,7 +859,6 @@ public BoundExpression StaticCall(WellKnownMember method, params BoundExpression public BoundExpression StaticCall(SpecialMember method, params BoundExpression[] args) { MethodSymbol methodSymbol = SpecialMethod(method); - Binder.ReportUseSite(methodSymbol, Diagnostics, Syntax); Debug.Assert(methodSymbol.IsStatic); return Call(null, methodSymbol, args); } @@ -1249,35 +1282,51 @@ public BoundTypeExpression Type(TypeSymbol type) return new BoundTypeExpression(Syntax, null, type) { WasCompilerGenerated = true }; } - public BoundExpression Typeof(WellKnownType type) + public BoundExpression Typeof(WellKnownType type, TypeSymbol systemType) { - return Typeof(WellKnownType(type)); + return Typeof(WellKnownType(type), systemType); } - public BoundExpression Typeof(TypeSymbol type) + public BoundExpression Typeof(TypeSymbol type, TypeSymbol systemType) { + Debug.Assert(systemType.ExtendedSpecialType == InternalSpecialType.System_Type || + systemType.Equals(Compilation.GetWellKnownType(CodeAnalysis.WellKnownType.System_Type), TypeCompareKind.AllIgnoreOptions)); + + MethodSymbol getTypeFromHandle; + + if (systemType.ExtendedSpecialType == InternalSpecialType.System_Type) + { + getTypeFromHandle = SpecialMethod(CodeAnalysis.SpecialMember.System_Type__GetTypeFromHandle); + } + else + { + getTypeFromHandle = WellKnownMethod(CodeAnalysis.WellKnownMember.System_Type__GetTypeFromHandle); + } + + Debug.Assert(TypeSymbol.Equals(systemType, getTypeFromHandle.ReturnType, TypeCompareKind.AllIgnoreOptions)); + return new BoundTypeOfOperator( Syntax, Type(type), - WellKnownMethod(CodeAnalysis.WellKnownMember.System_Type__GetTypeFromHandle), - WellKnownType(CodeAnalysis.WellKnownType.System_Type)) + getTypeFromHandle, + systemType) { WasCompilerGenerated = true }; } - public BoundExpression Typeof(TypeWithAnnotations type) + public BoundExpression Typeof(TypeWithAnnotations type, TypeSymbol systemType) { - return Typeof(type.Type); + return Typeof(type.Type, systemType); } - public ImmutableArray TypeOfs(ImmutableArray typeArguments) + public ImmutableArray TypeOfs(ImmutableArray typeArguments, TypeSymbol systemType) { - return typeArguments.SelectAsArray(Typeof); + return typeArguments.SelectAsArray(Typeof, systemType); } public BoundExpression TypeofDynamicOperationContextType() { Debug.Assert(this.CompilationState is { DynamicOperationContextType: { } }); - return Typeof(this.CompilationState.DynamicOperationContextType); + return Typeof(this.CompilationState.DynamicOperationContextType, WellKnownType(CodeAnalysis.WellKnownType.System_Type)); } public BoundExpression Sizeof(TypeSymbol type) @@ -1287,12 +1336,20 @@ public BoundExpression Sizeof(TypeSymbol type) internal BoundExpression ConstructorInfo(MethodSymbol ctor) { - return new BoundMethodInfo( + NamedTypeSymbol constructorInfo = WellKnownType(Microsoft.CodeAnalysis.WellKnownType.System_Reflection_ConstructorInfo); + + var result = new BoundMethodInfo( Syntax, ctor, - GetMethodFromHandleMethod(ctor.ContainingType), - WellKnownType(Microsoft.CodeAnalysis.WellKnownType.System_Reflection_ConstructorInfo)) + GetMethodFromHandleMethod(ctor.ContainingType, constructorInfo), + constructorInfo) { WasCompilerGenerated = true }; + +#if DEBUG + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + Debug.Assert(result.Type.IsErrorType() || result.Type!.IsDerivedFrom(result.GetMethodFromHandle!.ReturnType, TypeCompareKind.AllIgnoreOptions, ref discardedUseSiteInfo)); +#endif + return result; } public BoundExpression MethodDefIndex(MethodSymbol method) @@ -1377,7 +1434,7 @@ public BoundExpression SourceDocumentIndex(Cci.DebugSourceDocument document) { WasCompilerGenerated = true }; } - public BoundExpression MethodInfo(MethodSymbol method) + public BoundExpression MethodInfo(MethodSymbol method, TypeSymbol systemReflectionMethodInfo) { // The least overridden virtual method is only called for value type receivers // in special circumstances. These circumstances are exactly the checks performed by @@ -1388,12 +1445,18 @@ public BoundExpression MethodInfo(MethodSymbol method) method = method.GetConstructedLeastOverriddenMethod(this.CompilationState.Type, requireSameReturnType: true); } - return new BoundMethodInfo( + var result = new BoundMethodInfo( Syntax, method, - GetMethodFromHandleMethod(method.ContainingType), - WellKnownType(Microsoft.CodeAnalysis.WellKnownType.System_Reflection_MethodInfo)) + GetMethodFromHandleMethod(method.ContainingType, systemReflectionMethodInfo), + systemReflectionMethodInfo) { WasCompilerGenerated = true }; + +#if DEBUG + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + Debug.Assert(result.Type.IsErrorType() || result.Type!.IsDerivedFrom(result.GetMethodFromHandle!.ReturnType, TypeCompareKind.AllIgnoreOptions, ref discardedUseSiteInfo)); +#endif + return result; } public BoundExpression FieldInfo(FieldSymbol field) @@ -1406,12 +1469,28 @@ public BoundExpression FieldInfo(FieldSymbol field) { WasCompilerGenerated = true }; } - private MethodSymbol GetMethodFromHandleMethod(NamedTypeSymbol methodContainer) + private MethodSymbol GetMethodFromHandleMethod(NamedTypeSymbol methodContainer, TypeSymbol systemReflectionMethodOrConstructorInfo) { - return WellKnownMethod( - (methodContainer.AllTypeArgumentCount() == 0 && !methodContainer.IsAnonymousType) ? - CodeAnalysis.WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle : - CodeAnalysis.WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle2); + Debug.Assert(systemReflectionMethodOrConstructorInfo.ExtendedSpecialType == InternalSpecialType.System_Reflection_MethodInfo || + systemReflectionMethodOrConstructorInfo.Equals(Compilation.GetWellKnownType(CodeAnalysis.WellKnownType.System_Reflection_MethodInfo), TypeCompareKind.AllIgnoreOptions) || + systemReflectionMethodOrConstructorInfo.Equals(Compilation.GetWellKnownType(CodeAnalysis.WellKnownType.System_Reflection_ConstructorInfo), TypeCompareKind.AllIgnoreOptions)); + + bool isNotInGenericType = (methodContainer.AllTypeArgumentCount() == 0 && !methodContainer.IsAnonymousType); + + if (systemReflectionMethodOrConstructorInfo.ExtendedSpecialType == InternalSpecialType.System_Reflection_MethodInfo) + { + return SpecialMethod( + isNotInGenericType ? + CodeAnalysis.SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle : + CodeAnalysis.SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle2); + } + else + { + return WellKnownMethod( + isNotInGenericType ? + CodeAnalysis.WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle : + CodeAnalysis.WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle2); + } } private MethodSymbol GetFieldFromHandleMethod(NamedTypeSymbol fieldContainer) @@ -1486,7 +1565,7 @@ public BoundExpression ArrayOrEmpty(TypeSymbol elementType, ImmutableArray + diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 9a43c65d82474..acf8246550b90 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1257,13 +1257,14 @@ private IOperation CreateBoundCollectionExpressionElement(BoundCollectionExpress { return element is BoundCollectionExpressionSpreadElement spreadElement ? CreateBoundCollectionExpressionSpreadElement(expr, spreadElement) : - Create(Binder.GetUnderlyingCollectionExpressionElement(expr, (BoundExpression)element) ?? element); + Create(Binder.GetUnderlyingCollectionExpressionElement(expr, (BoundExpression)element, throwOnErrors: false)); } private ISpreadOperation CreateBoundCollectionExpressionSpreadElement(BoundCollectionExpression expr, BoundCollectionExpressionSpreadElement element) { - var iteratorBody = ((BoundExpressionStatement?)element.IteratorBody)?.Expression; - var iteratorItem = Binder.GetUnderlyingCollectionExpressionElement(expr, iteratorBody); + var iteratorItem = element.IteratorBody is { } iteratorBody ? + Binder.GetUnderlyingCollectionExpressionElement(expr, ((BoundExpressionStatement)iteratorBody).Expression, throwOnErrors: false) : + null; var collection = Create(element.Expression); SyntaxNode syntax = element.Syntax; bool isImplicit = element.WasCompilerGenerated; diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 0d94193097554..6ee66aec39cd7 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -5801,6 +5801,7 @@ private ScanTypeFlags ScanPossibleTypeArgumentList( } ScanTypeFlags result = ScanTypeFlags.GenericTypeOrExpression; + ScanTypeFlags lastScannedType; do { @@ -5819,7 +5820,8 @@ private ScanTypeFlags ScanPossibleTypeArgumentList( return result; } - switch (this.ScanType(out _)) + lastScannedType = this.ScanType(out _); + switch (lastScannedType) { case ScanTypeFlags.NotType: greaterThanToken = null; @@ -5918,6 +5920,25 @@ private ScanTypeFlags ScanPossibleTypeArgumentList( if (this.CurrentToken.Kind != SyntaxKind.GreaterThanToken) { + // Error recovery after missing > token: + + // In the case of an identifier, we assume that there could be a missing > token + // For example, we have reached C in X token between )( + // as the user probably wants to invoke X by X<(string, string)>() + if (lastScannedType is ScanTypeFlags.TupleType && this.CurrentToken.Kind is SyntaxKind.OpenParenToken) + { + greaterThanToken = this.EatToken(SyntaxKind.GreaterThanToken); + return result; + } + greaterThanToken = null; return ScanTypeFlags.NotType; } @@ -5968,7 +5989,34 @@ private void ParseTypeArgumentList(out SyntaxToken open, SeparatedSyntaxListBuil { break; } - else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType()) + + // We prefer early terminating the argument list over parsing until exhaustion + // for better error recovery + if (tokenBreaksTypeArgumentList(this.CurrentToken)) + { + break; + } + + // We are currently past parsing a type and we encounter an unexpected identifier token + // followed by tokens that are not part of a type argument list + // Example: List<(string a, string b) Method() { } + // current token: ^^^^^^ + if (this.CurrentToken.Kind is SyntaxKind.IdentifierToken && tokenBreaksTypeArgumentList(this.PeekToken(1))) + { + break; + } + + // This is for the case where we are in a this[] accessor, and the last one of the parameters in the parameter list + // is missing a > on its type + // Example: X this[IEnumerable + // current token: ^^^^^^^^^ + if (this.CurrentToken.Kind is SyntaxKind.IdentifierToken + && this.PeekToken(1).Kind is SyntaxKind.CloseBracketToken) + { + break; + } + + if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType()) { types.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); types.Add(this.ParseTypeArgument()); @@ -5980,6 +6028,57 @@ private void ParseTypeArgumentList(out SyntaxToken open, SeparatedSyntaxListBuil } close = this.EatToken(SyntaxKind.GreaterThanToken); + + static bool tokenBreaksTypeArgumentList(SyntaxToken token) + { + var contextualKind = SyntaxFacts.GetContextualKeywordKind(token.ValueText); + switch (contextualKind) + { + // Example: x is IEnumerable + case SyntaxKind.OrKeyword: + // Example: x is IEnumerable + // but since we do not look as far as possible to determine whether it is + // a tuple type or an argument list, we resort to considering it as an + // argument list + case SyntaxKind.OpenParenToken: + + // Example: IEnumerable() --- (< in ) + case SyntaxKind.LessThanToken: + // Example: Method(IEnumerable null; + case SyntaxKind.EqualsGreaterThanToken: + // Example: IEnumerable list, SyntaxKind expected) diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs index 9e230a8483aeb..b409d5a3abab0 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs @@ -1909,9 +1909,7 @@ private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxLi } // normal single line comment - this.ScanToEndOfLine(); - var text = TextWindow.GetText(false); - this.AddTrivia(SyntaxFactory.Comment(text), ref triviaList); + lexSingleLineComment(ref triviaList); onlyWhitespaceOnLine = false; break; } @@ -1938,12 +1936,27 @@ private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxLi // not trivia return; - case '@' when TextWindow.PeekChar(1) == '*': - // Razor comment. We pretend that it's a multi-line comment for error recovery, but it's an error case. - this.AddError(TextWindow.Position, width: 1, ErrorCode.ERR_UnexpectedCharacter, '@'); - lexMultiLineComment(ref triviaList, delimiter: '@'); - onlyWhitespaceOnLine = false; - break; + case '@': + if ((ch = TextWindow.PeekChar(1)) == '*') + { + // Razor comment. We pretend that it's a multi-line comment for error recovery, but it's an error case. + this.AddError(TextWindow.Position, width: 1, ErrorCode.ERR_UnexpectedCharacter, '@'); + lexMultiLineComment(ref triviaList, delimiter: '@'); + onlyWhitespaceOnLine = false; + break; + } + else if (ch == ':') + { + // Razor HTML transition. We pretend it's a single-line comment for error recovery. + this.AddError(TextWindow.Position, width: 1, ErrorCode.ERR_UnexpectedCharacter, '@'); + lexSingleLineComment(ref triviaList); + onlyWhitespaceOnLine = false; + break; + } + else + { + return; + } case '\r': case '\n': var endOfLine = this.ScanEndOfLine(); @@ -1992,6 +2005,13 @@ private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxLi } } + void lexSingleLineComment(ref SyntaxListBuilder triviaList) + { + this.ScanToEndOfLine(); + var text = TextWindow.GetText(false); + this.AddTrivia(SyntaxFactory.Comment(text), ref triviaList); + } + void lexMultiLineComment(ref SyntaxListBuilder triviaList, char delimiter) { bool isTerminated; diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index d3bb932bfcec2..a7eede3cf18d3 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -3,8 +3,18 @@ Microsoft.CodeAnalysis.CSharp.Conversion.IsCollectionExpression.get -> bool Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.ReadOnlyKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken refKindKeyword, Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax! Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax.WithReadOnlyKeyword(Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax! +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Dispose() -> void +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.ParseNextToken() -> Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.ResetTo(Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result result) -> void +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result.ContextualKind.get -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result.Result() -> void +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.Result.Token.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser.SkipForwardTo(int position) -> void static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel? semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax! node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IMethodSymbol? static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetElementConversion(this Microsoft.CodeAnalysis.Operations.ISpreadOperation! spread) -> Microsoft.CodeAnalysis.CSharp.Conversion +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CreateTokenParser(Microsoft.CodeAnalysis.Text.SourceText! sourceText, Microsoft.CodeAnalysis.CSharp.CSharpParseOptions? options = null) -> Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser! static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.CrefParameter(Microsoft.CodeAnalysis.SyntaxToken refKindKeyword, Microsoft.CodeAnalysis.SyntaxToken readOnlyKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type) -> Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterSyntax! [RSEXPERIMENTAL001]Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSemanticModel(Microsoft.CodeAnalysis.SyntaxTree! syntaxTree, Microsoft.CodeAnalysis.SemanticModelOptions options) -> Microsoft.CodeAnalysis.SemanticModel! [RSEXPERIMENTAL002]static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetInterceptorMethod(this Microsoft.CodeAnalysis.SemanticModel? semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.InvocationExpressionSyntax! node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.IMethodSymbol? diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.SymbolCollection.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.SymbolCollection.cs index ba0bcf1ea96f3..f274cc73a104e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.SymbolCollection.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.SymbolCollection.cs @@ -28,7 +28,7 @@ public bool ReportMissingOrErroneousSymbols(BindingDiagnosticBag diagnostics) ReportErrorOnSpecialMember(System_Object__Equals, SpecialMember.System_Object__Equals, diagnostics, ref hasErrors); ReportErrorOnSpecialMember(System_Object__ToString, SpecialMember.System_Object__ToString, diagnostics, ref hasErrors); ReportErrorOnSpecialMember(System_Object__GetHashCode, SpecialMember.System_Object__GetHashCode, diagnostics, ref hasErrors); - ReportErrorOnWellKnownMember(System_String__Format_IFormatProvider, WellKnownMember.System_String__Format_IFormatProvider, diagnostics, ref hasErrors); + ReportErrorOnSpecialMember(System_String__Format_IFormatProvider, SpecialMember.System_String__Format_IFormatProvider, diagnostics, ref hasErrors); // optional synthesized attributes: Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); @@ -178,7 +178,7 @@ public MethodSymbol System_Collections_Generic_EqualityComparer_T__get_Default public MethodSymbol System_String__Format_IFormatProvider { - get { return this.Compilation.GetWellKnownTypeMember(WellKnownMember.System_String__Format_IFormatProvider) as MethodSymbol; } + get { return this.Compilation.GetSpecialTypeMember(SpecialMember.System_String__Format_IFormatProvider) as MethodSymbol; } } #endregion diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index b74e501d2b7d8..f9d5d858f07da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -377,7 +377,7 @@ internal ErrorTypeSymbol CreateMultipleForwardingErrorTypeSymbol(ref MetadataTyp /// Lookup declaration for predefined CorLib type in this Assembly. /// /// The symbol for the pre-defined type or an error type if the type is not defined in the core library. - internal abstract NamedTypeSymbol GetDeclaredSpecialType(SpecialType type); + internal abstract NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type); /// /// Register declaration of predefined CorLib type in this Assembly. @@ -480,7 +480,7 @@ internal bool RuntimeSupportsInlineArrayTypes protected bool RuntimeSupportsFeature(SpecialMember feature) { // Keep in sync with VB's AssemblySymbol.RuntimeSupportsFeature - Debug.Assert((SpecialType)SpecialMembers.GetDescriptor(feature).DeclaringTypeId == SpecialType.System_Runtime_CompilerServices_RuntimeFeature); + Debug.Assert(SpecialMembers.GetDescriptor(feature).DeclaringSpecialType == SpecialType.System_Runtime_CompilerServices_RuntimeFeature); return GetSpecialType(SpecialType.System_Runtime_CompilerServices_RuntimeFeature) is { TypeKind: TypeKind.Class, IsStatic: true } && GetSpecialTypeMember(feature) is object; } @@ -584,7 +584,7 @@ bool IAssemblySymbolInternal.AreInternalsVisibleToThisAssembly(IAssemblySymbolIn /// Gets the symbol for the pre-defined type from core library associated with this assembly. /// /// The symbol for the pre-defined type or an error type if the type is not defined in the core library. - internal NamedTypeSymbol GetSpecialType(SpecialType type) + internal NamedTypeSymbol GetSpecialType(ExtendedSpecialType type) { return CorLibrary.GetDeclaredSpecialType(type); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs index b3796f4ed27ca..65292086a7814 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_WellKnownMembers.cs @@ -96,9 +96,9 @@ private void SetUsesNullableAttributes() } MemberDescriptor descriptor = WellKnownMembers.GetDescriptor(member); - NamedTypeSymbol type = descriptor.DeclaringTypeId <= (int)SpecialType.Count - ? this.GetSpecialType((SpecialType)descriptor.DeclaringTypeId) - : this.GetWellKnownType((WellKnownType)descriptor.DeclaringTypeId); + NamedTypeSymbol type = descriptor.IsSpecialTypeMember + ? this.GetSpecialType(descriptor.DeclaringSpecialType) + : this.GetWellKnownType(descriptor.DeclaringWellKnownType); Symbol? result = null; if (!type.IsErrorType()) @@ -1223,7 +1223,7 @@ protected override bool MatchArrayRank(TypeSymbol type, int countOfDimensions) protected override bool MatchTypeToTypeId(TypeSymbol type, int typeId) { - if ((int)type.OriginalDefinition.SpecialType == typeId) + if ((int)type.OriginalDefinition.ExtendedSpecialType == typeId) { if (type.IsDefinition) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index dc4c06cecd0a2..ea2cd5b3dbaa5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -34,7 +34,7 @@ internal abstract class PENamedTypeSymbol : NamedTypeSymbol private readonly TypeDefinitionHandle _handle; private readonly string _name; private readonly TypeAttributes _flags; - private readonly SpecialType _corTypeId; + private readonly ExtendedSpecialType _corTypeId; /// /// A set of all the names of the members in this type. @@ -356,7 +356,7 @@ private PENamedTypeSymbol( } } - public override SpecialType SpecialType + public override ExtendedSpecialType ExtendedSpecialType { get { @@ -2136,7 +2136,7 @@ protected virtual DiagnosticInfo GetUseSiteDiagnosticImpl() if ((object)missingType != null && missingType.Arity == 0) { string emittedName = MetadataHelpers.BuildQualifiedName(missingType.NamespaceName, missingType.MetadataName); - switch (SpecialTypes.GetTypeFromMetadataName(emittedName)) + switch ((SpecialType)SpecialTypes.GetTypeFromMetadataName(emittedName)) { case SpecialType.System_Enum: case SpecialType.System_MulticastDelegate: diff --git a/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs index b428cbefcd912..f9598b0f2e433 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MetadataOrSourceAssemblySymbol.cs @@ -42,7 +42,7 @@ internal abstract class MetadataOrSourceAssemblySymbol /// /// /// - internal sealed override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) + internal sealed override NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type) { #if DEBUG foreach (var module in this.Modules) @@ -79,7 +79,7 @@ internal sealed override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type /// internal sealed override void RegisterDeclaredSpecialType(NamedTypeSymbol corType) { - SpecialType typeId = corType.SpecialType; + ExtendedSpecialType typeId = corType.ExtendedSpecialType; Debug.Assert(typeId != SpecialType.None); Debug.Assert(ReferenceEquals(corType.ContainingAssembly, this)); Debug.Assert(corType.ContainingModule.Ordinal == 0); @@ -88,7 +88,7 @@ internal sealed override void RegisterDeclaredSpecialType(NamedTypeSymbol corTyp if (_lazySpecialTypes == null) { Interlocked.CompareExchange(ref _lazySpecialTypes, - new NamedTypeSymbol[(int)SpecialType.Count + 1], null); + new NamedTypeSymbol[(int)InternalSpecialType.NextAvailable], null); } if ((object)Interlocked.CompareExchange(ref _lazySpecialTypes[(int)typeId], corType, null) != null) @@ -100,7 +100,7 @@ internal sealed override void RegisterDeclaredSpecialType(NamedTypeSymbol corTyp else { Interlocked.Increment(ref _cachedSpecialTypes); - Debug.Assert(_cachedSpecialTypes > 0 && _cachedSpecialTypes <= (int)SpecialType.Count); + Debug.Assert(_cachedSpecialTypes > 0 && _cachedSpecialTypes < (int)InternalSpecialType.NextAvailable); } } @@ -112,7 +112,7 @@ internal override bool KeepLookingForDeclaredSpecialTypes { get { - return ReferenceEquals(this.CorLibrary, this) && _cachedSpecialTypes < (int)SpecialType.Count; + return ReferenceEquals(this.CorLibrary, this) && _cachedSpecialTypes < (int)InternalSpecialType.NextAvailable - 1; } } @@ -202,7 +202,7 @@ internal override Symbol GetDeclaredSpecialTypeMember(SpecialMember member) } var descriptor = SpecialMembers.GetDescriptor(member); - NamedTypeSymbol type = GetDeclaredSpecialType((SpecialType)descriptor.DeclaringTypeId); + NamedTypeSymbol type = GetDeclaredSpecialType(descriptor.DeclaringSpecialType); Symbol result = null; if (!type.IsErrorType()) diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs index d4d76cbab951f..91cb0f329d381 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs @@ -179,7 +179,7 @@ internal override NamedTypeSymbol LookupDeclaredOrForwardedTopLevelMetadataType( #nullable disable - internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) + internal override NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type) { throw ExceptionUtilities.Unreachable(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs index d9d5005291028..0210e04ae4d12 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingCorLibrarySymbol.cs @@ -57,7 +57,7 @@ internal override TypeConversions TypeConversions /// called if it is know that this is the Cor Library (mscorlib). /// /// - internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) + internal override NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type) { #if DEBUG foreach (var module in this.Modules) @@ -69,7 +69,7 @@ internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) if (_lazySpecialTypes == null) { Interlocked.CompareExchange(ref _lazySpecialTypes, - new NamedTypeSymbol[(int)SpecialType.Count + 1], null); + new NamedTypeSymbol[(int)InternalSpecialType.NextAvailable], null); } if ((object)_lazySpecialTypes[(int)type] == null) diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs index 4eb2298265afe..59443d5665548 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingMetadataTypeSymbol.cs @@ -146,7 +146,7 @@ internal sealed class TopLevel : MissingMetadataTypeSymbol private NamespaceSymbol? _lazyContainingNamespace; /// - /// Either , , or -1 if not initialized. + /// Either , , , or -1 if not initialized. /// private int _lazyTypeId; @@ -160,7 +160,7 @@ public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, DiagnosticIn { } - public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, SpecialType specialType, DiagnosticInfo? errorInfo = null) + public TopLevel(ModuleSymbol module, ref MetadataTypeName fullName, ExtendedSpecialType specialType, DiagnosticInfo? errorInfo = null) : this(module, ref fullName, (int)specialType, errorInfo) { } @@ -287,7 +287,7 @@ private int TypeId { if (_lazyTypeId == -1) { - SpecialType typeId = SpecialType.None; + ExtendedSpecialType typeId = default; AssemblySymbol containingAssembly = _containingModule.ContainingAssembly; @@ -305,12 +305,12 @@ private int TypeId } } - public override SpecialType SpecialType + public override ExtendedSpecialType ExtendedSpecialType { get { int typeId = TypeId; - return (typeId >= (int)WellKnownType.First) ? SpecialType.None : (SpecialType)_lazyTypeId; + return (typeId >= (int)WellKnownType.First) ? SpecialType.None : (ExtendedSpecialType)typeId; } } @@ -434,11 +434,11 @@ public override Symbol ContainingSymbol } } - public override SpecialType SpecialType + public override ExtendedSpecialType ExtendedSpecialType { get { - return SpecialType.None; // do not have nested types among CORE types yet. + return default; // do not have nested types among CORE types yet. } } diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index eab38790cbb67..4bf3c91ad7608 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -47,7 +47,7 @@ internal NativeIntegerTypeSymbol(NamedTypeSymbol underlyingType) : base(underlyi internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics => _underlyingType.BaseTypeNoUseSiteDiagnostics; - public override SpecialType SpecialType => _underlyingType.SpecialType; + public override ExtendedSpecialType ExtendedSpecialType => _underlyingType.ExtendedSpecialType; public override IEnumerable MemberNames => GetMembers().Select(m => m.Name); diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index 4d7e123e1468d..d5f5bcbe61e54 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -109,10 +109,12 @@ private ReducedExtensionMethodSymbol(MethodSymbol reducedFrom) /// are not satisfied, the return value is null. /// /// Compilation used to check constraints. The latest language version is assumed if this is null. - private static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, + internal static MethodSymbol InferExtensionMethodTypeArguments(MethodSymbol method, TypeSymbol thisType, CSharpCompilation compilation, ref CompoundUseSiteInfo useSiteInfo, out bool wasFullyInferred) { Debug.Assert(method.IsExtensionMethod); + Debug.Assert(method.MethodKind != MethodKind.ReducedExtension); + Debug.Assert(method.ParameterCount > 0); Debug.Assert((object)thisType != null); if (!method.IsGenericMethod || method != method.ConstructedFrom) diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs index 51bcaa770622a..72e7e23ba7ad5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs @@ -218,7 +218,7 @@ public override ImmutableArray GetAttributes() /// /// /// - internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) + internal override NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type) { // Cor library should not have any references and, therefore, should never be // wrapped by a RetargetingAssemblySymbol. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 85eba6bdf14bb..27167398d80fe 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -1579,7 +1579,7 @@ void validateParamsType(BindingDiagnosticBag diagnostics) checkIsAtLeastAsVisible(syntax, binder, constructor, diagnostics); } - if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, Type, elementType, out ImmutableArray addMethods, diagnostics)) + if (!binder.HasCollectionExpressionApplicableAddMethod(syntax, Type, out ImmutableArray addMethods, diagnostics)) { return; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index dce4856200ae3..97628686b76fa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -80,9 +80,9 @@ private struct Flags private const int HasPrimaryConstructorBit = 1 << HasPrimaryConstructorOffset; - public SpecialType SpecialType + public ExtendedSpecialType ExtendedSpecialType { - get { return (SpecialType)((_flags >> SpecialTypeOffset) & SpecialTypeMask); } + get { return (ExtendedSpecialType)((_flags >> SpecialTypeOffset) & SpecialTypeMask); } } public ManagedKind ManagedKind @@ -110,12 +110,14 @@ public TypeKind TypeKind static Flags() { // Verify masks are sufficient for values. + _ = new int[SpecialTypeMask - (int)InternalSpecialType.NextAvailable + 1]; Debug.Assert(EnumUtilities.ContainsAllValues(SpecialTypeMask)); + Debug.Assert(EnumUtilities.ContainsAllValues(SpecialTypeMask)); //This assert might false fail in the future, we don't really need to be able to represent NextAvailable Debug.Assert(EnumUtilities.ContainsAllValues(NullableContextMask)); } #endif - public Flags(SpecialType specialType, TypeKind typeKind, bool hasPrimaryConstructor) + public Flags(ExtendedSpecialType specialType, TypeKind typeKind, bool hasPrimaryConstructor) { int specialTypeInt = ((int)specialType & SpecialTypeMask) << SpecialTypeOffset; int typeKindInt = ((int)typeKind & TypeKindMask) << TypeKindOffset; @@ -251,8 +253,8 @@ internal SourceMemberContainerTypeSymbol( _declModifiers = modifiers; var specialType = access == (int)DeclarationModifiers.Public - ? MakeSpecialType() - : SpecialType.None; + ? MakeExtendedSpecialType() + : default; _flags = new Flags(specialType, typeKind, declaration.HasPrimaryConstructor); Debug.Assert(typeKind is TypeKind.Struct or TypeKind.Class || !HasPrimaryConstructor); @@ -266,7 +268,7 @@ internal SourceMemberContainerTypeSymbol( state.NotePartComplete(CompletionPart.TypeArguments); // type arguments need not be computed separately } - private SpecialType MakeSpecialType() + private ExtendedSpecialType MakeExtendedSpecialType() { // check if this is one of the COR library types if (ContainingSymbol.Kind == SymbolKind.Namespace && @@ -280,7 +282,7 @@ private SpecialType MakeSpecialType() } else { - return SpecialType.None; + return default; } } @@ -770,11 +772,11 @@ public sealed override Symbol ContainingSymbol #region Flags Encoded Properties - public override SpecialType SpecialType + public override ExtendedSpecialType ExtendedSpecialType { get { - return _flags.SpecialType; + return _flags.ExtendedSpecialType; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs index 7f6f67303b585..bf7b4599dc6e0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs @@ -76,6 +76,17 @@ internal sealed class SynthesizedReadOnlyListTypeSymbol : NamedTypeSymbol { SpecialMember.System_Collections_IEnumerable__GetEnumerator, SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, + SpecialMember.System_Collections_Generic_ICollection_T__Count, + SpecialMember.System_Collections_Generic_ICollection_T__IsReadOnly, + SpecialMember.System_Collections_Generic_ICollection_T__Add, + SpecialMember.System_Collections_Generic_ICollection_T__Clear, + SpecialMember.System_Collections_Generic_ICollection_T__Contains, + SpecialMember.System_Collections_Generic_ICollection_T__CopyTo, + SpecialMember.System_Collections_Generic_ICollection_T__Remove, + SpecialMember.System_Collections_Generic_IList_T__get_Item, + SpecialMember.System_Collections_Generic_IList_T__IndexOf, + SpecialMember.System_Collections_Generic_IList_T__Insert, + SpecialMember.System_Collections_Generic_IList_T__RemoveAt, }; private static readonly WellKnownMember[] s_requiredWellKnownMembers = new[] @@ -94,24 +105,13 @@ internal sealed class SynthesizedReadOnlyListTypeSymbol : NamedTypeSymbol WellKnownMember.System_Collections_IList__Insert, WellKnownMember.System_Collections_IList__Remove, WellKnownMember.System_Collections_IList__RemoveAt, - WellKnownMember.System_Collections_Generic_ICollection_T__Count, - WellKnownMember.System_Collections_Generic_ICollection_T__IsReadOnly, - WellKnownMember.System_Collections_Generic_ICollection_T__Add, - WellKnownMember.System_Collections_Generic_ICollection_T__Clear, - WellKnownMember.System_Collections_Generic_ICollection_T__Contains, - WellKnownMember.System_Collections_Generic_ICollection_T__CopyTo, - WellKnownMember.System_Collections_Generic_ICollection_T__Remove, - WellKnownMember.System_Collections_Generic_IList_T__get_Item, - WellKnownMember.System_Collections_Generic_IList_T__IndexOf, - WellKnownMember.System_Collections_Generic_IList_T__Insert, - WellKnownMember.System_Collections_Generic_IList_T__RemoveAt, WellKnownMember.System_NotSupportedException__ctor, }; - private static readonly WellKnownMember[] s_readOnlyInterfacesWellKnownMembers = new[] + private static readonly SpecialMember[] s_readOnlyInterfacesWellKnownMembers = new[] { - WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count, - WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item, + SpecialMember.System_Collections_Generic_IReadOnlyCollection_T__Count, + SpecialMember.System_Collections_Generic_IReadOnlyList_T__get_Item, }; private static readonly WellKnownMember[] s_requiredWellKnownMembersUnknownLength = new[] @@ -193,7 +193,7 @@ internal static NamedTypeSymbol Create(SourceModuleSymbol containingModule, stri { foreach (var member in s_readOnlyInterfacesWellKnownMembers) { - diagnosticInfo = getWellKnownTypeMemberDiagnosticInfo(compilation, member); + diagnosticInfo = getSpecialTypeMemberDiagnosticInfo(compilation, member); if (diagnosticInfo is { }) { break; @@ -400,69 +400,69 @@ private SynthesizedReadOnlyListTypeSymbol(SourceModuleSymbol containingModule, s addProperty(membersBuilder, new SynthesizedReadOnlyListProperty( this, - ((PropertySymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count)!).AsMember(iReadOnlyCollectionT), + ((PropertySymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IReadOnlyCollection_T__Count)!).AsMember(iReadOnlyCollectionT), generateCount)); addProperty(membersBuilder, new SynthesizedReadOnlyListProperty( this, - ((PropertySymbol)((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item)!).AssociatedSymbol).AsMember(iReadOnlyListT), + ((PropertySymbol)((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IReadOnlyList_T__get_Item)!).AssociatedSymbol).AsMember(iReadOnlyListT), generateIndexer)); } addProperty(membersBuilder, new SynthesizedReadOnlyListProperty( this, - ((PropertySymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__Count)!).AsMember(iCollectionT), + ((PropertySymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__Count)!).AsMember(iCollectionT), generateCount)); addProperty(membersBuilder, new SynthesizedReadOnlyListProperty( this, - ((PropertySymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__IsReadOnly)!).AsMember(iCollectionT), + ((PropertySymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__IsReadOnly)!).AsMember(iCollectionT), generateIsReadOnly)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__Add)!).AsMember(iCollectionT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__Add)!).AsMember(iCollectionT), generateNotSupportedException)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__Clear)!).AsMember(iCollectionT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__Clear)!).AsMember(iCollectionT), generateNotSupportedException)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__Contains)!).AsMember(iCollectionT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__Contains)!).AsMember(iCollectionT), generateContains)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__CopyTo)!).AsMember(iCollectionT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__CopyTo)!).AsMember(iCollectionT), generateCopyTo)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_ICollection_T__Remove)!).AsMember(iCollectionT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_ICollection_T__Remove)!).AsMember(iCollectionT), generateNotSupportedException)); addProperty(membersBuilder, new SynthesizedReadOnlyListProperty( this, - ((PropertySymbol)((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IList_T__get_Item)!).AssociatedSymbol).AsMember(iListT), + ((PropertySymbol)((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IList_T__get_Item)!).AssociatedSymbol).AsMember(iListT), generateIndexer, generateNotSupportedException)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IList_T__IndexOf)!).AsMember(iListT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IList_T__IndexOf)!).AsMember(iListT), generateIndexOf)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IList_T__Insert)!).AsMember(iListT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IList_T__Insert)!).AsMember(iListT), generateNotSupportedException)); membersBuilder.Add( new SynthesizedReadOnlyListMethod( this, - ((MethodSymbol)compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_IList_T__RemoveAt)!).AsMember(iListT), + ((MethodSymbol)compilation.GetSpecialTypeMember(SpecialMember.System_Collections_Generic_IList_T__RemoveAt)!).AsMember(iListT), generateNotSupportedException)); _members = membersBuilder.ToImmutableAndFree(); @@ -599,7 +599,7 @@ static BoundStatement generateCopyTo(SyntheticBoundNodeFactory f, MethodSymbol m { if (!interfaceMethod.ContainingType.IsGenericType) { - var arraySetValueMethod = (MethodSymbol)method.DeclaringCompilation.GetWellKnownTypeMember(WellKnownMember.System_Array__SetValue)!; + var arraySetValueMethod = (MethodSymbol)method.DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_Array__SetValue)!; // param0.SetValue((object)_item, param1) statement = f.ExpressionStatement( @@ -780,7 +780,7 @@ public static bool CanCreateSingleElement(CSharpCompilation compilation) return compilation.GetWellKnownType(WellKnownType.System_IndexOutOfRangeException) is not MissingMetadataTypeSymbol && compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_EqualityComparer_T) is not MissingMetadataTypeSymbol && compilation.GetWellKnownTypeMember(WellKnownMember.System_IndexOutOfRangeException__ctor) is not null - && compilation.GetWellKnownTypeMember(WellKnownMember.System_Array__SetValue) is not null + && compilation.GetSpecialTypeMember(SpecialMember.System_Array__SetValue) is not null && compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_EqualityComparer_T__get_Default) is not null && compilation.GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_EqualityComparer_T__Equals) is not null && compilation.GetSpecialType(SpecialType.System_IDisposable) is not MissingMetadataTypeSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index 0ac9cd56a77fe..c64029e821ca8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -170,7 +170,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, try { F.CurrentFunction = this; - F.CloseMethod(F.Block(F.Return(F.Typeof(ContainingType)))); + F.CloseMethod(F.Block(F.Return(F.Typeof(ContainingType, ReturnType)))); } catch (SyntheticBoundNodeFactory.MissingPredefinedMember ex) { diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 713a45870fe39..4b875082e4b5f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -490,14 +490,16 @@ internal TypeSymbol() /// /// Not preserved in types constructed from this one. /// - public virtual SpecialType SpecialType + public virtual ExtendedSpecialType ExtendedSpecialType { get { - return SpecialType.None; + return default; } } + public SpecialType SpecialType => (SpecialType)ExtendedSpecialType; + /// /// Gets corresponding primitive type code for this type declaration. /// diff --git a/src/Compilers/CSharp/Portable/Syntax/SourceTextTokenParser.cs b/src/Compilers/CSharp/Portable/Syntax/SourceTextTokenParser.cs new file mode 100644 index 0000000000000..8c701e1797c1d --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/SourceTextTokenParser.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more 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 InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax; + +namespace Microsoft.CodeAnalysis.CSharp; + +/// +/// A token parser that can be used to parse tokens continuously from a source. This parser parses continuously; every call to +/// will return the next token in the source text, starting from position 0. +/// can be used to skip forward in the file to a specific position, and can be used to reset the parser +/// to a previously-lexed position. +/// +/// +/// This type is safe to double dispose, but it is not safe to use after it has been disposed. Behavior in such scenarios +/// is undefined. +/// +/// This type is not thread safe. +/// +public sealed class SyntaxTokenParser : IDisposable +{ + private InternalSyntax.Lexer _lexer; + + internal SyntaxTokenParser(InternalSyntax.Lexer lexer) + { + _lexer = lexer; + } + + public void Dispose() + { + var lexer = Interlocked.CompareExchange(ref _lexer!, null, _lexer); + lexer?.Dispose(); + } + + /// + /// Parse the next token from the input at the current position. This will advance the internal position of the token parser to the + /// end of the returned token, including any trailing trivia. + /// + /// + /// The returned token will have a parent of . + /// + /// Since this API does not create a that owns all produced tokens, + /// the API may yield surprising results for + /// the produced tokens and its behavior is generally unspecified. + /// + public Result ParseNextToken() + { + var startingDirectiveStack = _lexer.Directives; + var startingPosition = _lexer.TextWindow.Position; + var token = _lexer.Lex(InternalSyntax.LexerMode.Syntax); + return new Result(new SyntaxToken(parent: null, token, startingPosition, index: 0), startingDirectiveStack); + } + + /// + /// Skip forward in the input to the specified position. Current directive state is preserved during the skip. + /// + /// The absolute location in the original text to move to. + /// If the given position is less than the current position of the lexer. + public void SkipForwardTo(int position) + { + if (position < _lexer.TextWindow.Position) + throw new ArgumentOutOfRangeException(nameof(position)); + + _lexer.TextWindow.Reset(position); + } + + /// + /// Resets the token parser to an earlier position in the input. The parser is reset to the start of the token that was previously + /// parsed, before any leading trivia, with the directive state that existed at the start of the token. + /// + public void ResetTo(Result result) + { + _lexer.Reset(result.Token.Position, result.ContextStartDirectiveStack); + } + + /// + /// The result of a call to . This is also a context object that can be used to reset the parser to + /// before the token it represents was parsed. + /// + /// + /// This type is not default safe. Attempts to use default(Result) will result in undefined behavior. + /// + public readonly struct Result + { + /// + /// The token that was parsed. + /// + public readonly SyntaxToken Token { get; } + + /// + /// If the parsed token is potentially a contextual keyword, this will return the contextual kind of the token. Otherwise, it + /// will return . + /// + public readonly SyntaxKind ContextualKind + { + get + { + var contextualKind = Token.ContextualKind(); + return contextualKind == Token.Kind() ? SyntaxKind.None : contextualKind; + } + } + + internal readonly InternalSyntax.DirectiveStack ContextStartDirectiveStack; + + internal Result(SyntaxToken token, InternalSyntax.DirectiveStack contextStartDirectiveStack) + { + Token = token; + ContextStartDirectiveStack = contextStartDirectiveStack; + } + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index ab864b3398b07..c8d16b0633525 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1672,6 +1672,16 @@ public static IEnumerable ParseTokens(string text, int offset = 0, } } + /// + /// Creates a token parser that can be used to parse tokens from a given source text. + /// + /// The source to parse tokens from. + /// Parse options for the source. + public static SyntaxTokenParser CreateTokenParser(SourceText sourceText, CSharpParseOptions? options = null) + { + return new SyntaxTokenParser(new InternalSyntax.Lexer(sourceText, options ?? CSharpParseOptions.Default)); + } + /// /// Parse a NameSyntax node using the grammar rule for names. /// diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index ba9c1c76ae68f..69c3de99c3b5a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -77,6 +77,9 @@ public enum SyntaxKind : ushort /// Represents .. token. DotDotToken = 8222, + // Values ranging from 8193 (TildeToken) to 8287 (GreaterThanGreaterThanGreaterThanEqualsToken) are reserved for punctuation kinds. + // This gap is included within that range. So if you add a value here make sure `SyntaxFacts.GetPunctuationKinds` includes it in the returned enumeration + // additional xml tokens /// Represents /> token. SlashGreaterThanToken = 8232, // xml empty element end @@ -95,6 +98,9 @@ public enum SyntaxKind : ushort /// Represents ?> token. XmlProcessingInstructionEndToken = 8239, // ?> + // Values ranging from 8193 (TildeToken) to 8287 (GreaterThanGreaterThanGreaterThanEqualsToken) are reserved for punctuation kinds. + // This gap is included within that range. So if you add a value here make sure `SyntaxFacts.GetPunctuationKinds` includes it in the returned enumeration + // compound punctuation /// Represents || token. BarBarToken = 8260, diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 652092a89b94f..6a19e29e2e469 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.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.Generic; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp { @@ -17,6 +19,7 @@ public static IEnumerable GetReservedKeywordKinds() { for (int i = (int)SyntaxKind.BoolKeyword; i <= (int)SyntaxKind.ImplicitKeyword; i++) { + Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)); yield return (SyntaxKind)i; } } @@ -142,9 +145,10 @@ public static IEnumerable GetPreprocessorKeywordKinds() yield return SyntaxKind.TrueKeyword; yield return SyntaxKind.FalseKeyword; yield return SyntaxKind.DefaultKeyword; - yield return SyntaxKind.HiddenKeyword; + for (int i = (int)SyntaxKind.ElifKeyword; i <= (int)SyntaxKind.RestoreKeyword; i++) { + Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)); yield return (SyntaxKind)i; } } @@ -172,10 +176,26 @@ private static bool IsDebuggerSpecialPunctuation(SyntaxKind kind) public static IEnumerable GetPunctuationKinds() { - for (int i = (int)SyntaxKind.TildeToken; i <= (int)SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; i++) + for (int i = (int)SyntaxKind.TildeToken; i <= (int)SyntaxKind.DotDotToken; i++) + { + Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)); + yield return (SyntaxKind)i; + } + + for (int i = (int)SyntaxKind.SlashGreaterThanToken; i <= (int)SyntaxKind.XmlProcessingInstructionEndToken; i++) { + Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)); yield return (SyntaxKind)i; } + + for (int i = (int)SyntaxKind.BarBarToken; i <= (int)SyntaxKind.QuestionQuestionEqualsToken; i++) + { + Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)); + yield return (SyntaxKind)i; + } + + yield return SyntaxKind.GreaterThanGreaterThanGreaterThanToken; + yield return SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; } public static bool IsPunctuationOrKeyword(SyntaxKind kind) @@ -1148,7 +1168,12 @@ public static IEnumerable GetContextualKeywordKinds() { for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.FileKeyword; i++) { - yield return (SyntaxKind)i; + // 8441 corresponds to a deleted kind (DataKeyword) that was previously shipped. + if (i != 8441) + { + Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)); + yield return (SyntaxKind)i; + } } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 967e0b13e562a..94e72ea2f90a1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Příkaz lock pro hodnotu typu System.Threading.Lock nejde použít v asynchronních metodách nebo asynchronních výrazech lambda. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Argumenty typu pro metodu {0} nelze odvodit z použití, protože je použit argument s dynamickým typem a metoda má parametr kolekce parametrů mimo pole. Zkuste argumenty typu zadat explicitně. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Typ výrazu kolekce {0} musí mít instanci nebo metodu rozšíření Přidat, kterou lze volat jedním argumentem. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Typ výrazu kolekce musí mít použitelný konstruktor, který lze volat bez argumentů. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + Cíl výrazu kolekce {0} nemá žádný typ elementu. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Nepoužívejte System.ParamArrayAttribute/System.Runtime.CompilerServices.ParamCollectionAttribute. Použijte místo něj klíčové slovo params. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Modifikátory nejde umístit na použití deklarací. @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Nejednoznačnost mezi rozbalenými a normálními tvary parametru kolekce parametrů mimo pole {0}, jediný odpovídající argument má dynamický typ. Zvažte přetypování dynamického argumentu. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Konstruktor {0} ponechá požadovaný člen {1} neinicializovaný. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Strom výrazů nesmí obsahovat rozšířenou formu parametru kolekce parametrů mimo pole. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + {0} neobsahuje definici vhodné instance metody Přidat. Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Vytvoření kolekce parametrů {0} vede k nekonečnému řetězu volání konstruktoru {1}. Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Typ kolekce parametrů mimo pole musí mít použitelný konstruktor, který lze volat bez argumentů. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + Metoda {0} nemůže být méně viditelná než člen s kolekcí parametrů {1}. The params parameter must have a valid collection type - The params parameter must have a valid collection type + Parametr params musí mít platný typ kolekce. @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + implicitní inicializační výraz indexeru @@ -2304,7 +2304,7 @@ Lock object - Lock object + Zamknout objekt @@ -2319,7 +2319,7 @@ params collections - params collections + kolekce parametrů @@ -2369,7 +2369,7 @@ string escape character - string escape character + řídicí znak řetězce @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Hodnota typu System.Threading.Lock převedená na jiný typ použije pravděpodobně nezamýšlené zamykání na základě monitorování v příkazu lock. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Hodnota typu System.Threading.Lock převedená na jiný typ použije pravděpodobně nezamýšlené zamykání na základě monitorování v příkazu lock. @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno nebo více přetížení konstruktoru s parametrem kolekce parametrů mimo pole může být použitelné pouze v rozbalené podobě, která není během dynamického odesílání podporována. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno nebo více přetížení konstruktoru s parametrem kolekce parametrů mimo pole může být použitelné pouze v rozbalené podobě, která není během dynamického odesílání podporována. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno nebo více přetížení indexeru s parametrem kolekce parametrů mimo pole může být použitelné pouze v rozbalené podobě, která není během dynamického odesílání podporována. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno nebo více přetížení indexeru s parametrem kolekce parametrů mimo pole může být použitelné pouze v rozbalené podobě, která není během dynamického odesílání podporována. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno nebo více přetížení metody{0} s parametrem kolekce parametrů mimo pole může být použitelné pouze v rozbalené podobě, která není během dynamického odesílání podporována. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno nebo více přetížení metody s parametrem kolekce parametrů mimo pole může být použitelné pouze v rozbalené podobě, která není během dynamického odesílání podporována. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - Proměnná {0} {1}, která nemůže být null, musí při ukončování konstruktoru obsahovat hodnotu, která není null. Zvažte možnost deklarovat {0} jako proměnnou s možnou hodnotou null. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + Proměnná {0} {1}, která nemůže být null, musí při ukončování konstruktoru obsahovat hodnotu, která není null. Zvažte možnost deklarovat {0} jako proměnnou s možnou hodnotou null. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Pole, které nemůže být null, musí při ukončování konstruktoru obsahovat hodnotu, která není null. Zvažte možnost deklarovat ho jako pole s možnou hodnotou null. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Pole, které nemůže být null, musí při ukončování konstruktoru obsahovat hodnotu, která není null. Zvažte možnost deklarovat ho jako pole s možnou hodnotou null. @@ -10963,7 +10963,7 @@ Potlačení upozornění zvažte jenom v případě, když určitě nechcete če Cannot specify a default value for a parameter collection - Nejde zadat výchozí hodnotu pro pole parametrů. + Nejde zadat výchozí hodnotu pro kolekci parametrů. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index ce61deae0070c..a4104f094b707 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Eine Sperranweisung für einen Wert vom Typ „System.Threading.Lock“ kann nicht in asynchronen Methoden oder asynchronen Lambdaausdrücken verwendet werden. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Die Typargumente für die Methode "{0}" können nicht aus der Verwendung abgeleitet werden, da ein Argument mit dynamischem Typ verwendet wird und die Methode einen nicht arraybasierten Params-Auflistungsparameter aufweist. Geben Sie die Typargumente explizit an. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Der Auflistungsausdruckstyp muss über einen anwendbaren Konstruktor verfügen, der ohne Argumente aufgerufen werden kann. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + Das Auflistungsausdrucksziel „{0}“ weist keinen Elementtyp auf. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Verwenden Sie nicht "System.ParamArrayAttribute"/"System.Runtime.CompilerServices.ParamCollectionAttribute". Verwenden Sie stattdessen das Schlüsselwort "Params". @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Modifizierer können nicht auf Using-Deklarationen platziert werden @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Mehrdeutigkeit zwischen erweiterten und normalen Formen des nicht arraybasierten Params-Auflistungsparameter von "{0}". Das einzige entsprechende Argument hat den Typ "dynamic". Erwägen Sie die Umwandlung des dynamischen Arguments. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Der Konstruktor "{0}" lässt den erforderlichen Member "{1}" deinitialisiert. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Eine Ausdrucksstruktur darf keine erweiterte Form eines nicht arraybasierten Params-Auflistungsparameter enthalten. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + "{0}" enthält keine Definition für eine "Add"-Methode für geeignete Instanzen Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Die Erstellung der Params-Auflistung "{0}" führt zu einer unendlichen Aufrufkette des Konstruktors "{1}". Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Nicht arraybasierten Params-Auflistungsparametertypen müssen über einen anwendbaren Konstruktor verfügen, der ohne Argumente aufgerufen werden kann. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + Die Methode "{0}" darf nicht weniger sichtbar sein als der Member mit der Params-Auflistung "{1}". The params parameter must have a valid collection type - The params parameter must have a valid collection type + Der Params-Parameter muss einen gültigen Sammlungstyp aufweisen @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + impliziter Indexerinitialisierer @@ -2304,7 +2304,7 @@ Lock object - Lock object + Objekt sperren @@ -2319,7 +2319,7 @@ params collections - params collections + Params-Sammlungen @@ -2369,7 +2369,7 @@ string escape character - string escape character + Zeichenfolge – Escapezeichen @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Ein Wert vom Typ „System.Threading.Lock“, der in einen anderen Typ konvertiert wurde, verwendet wahrscheinlich eine unbeabsichtigte monitorbasierte Sperrung in der Anweisung „lock“. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Ein Wert vom Typ „System.Threading.Lock“, der in einen anderen Typ konvertiert wurde, verwendet wahrscheinlich eine unbeabsichtigte monitorbasierte Sperrung in der Anweisung „lock“. @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Mindestens eine Konstruktorüberladung mit nicht arraybasierten Params-Auflistungsparametern kann nur in erweiterter Form anwendbar sein, was während der dynamischen Verteilung nicht unterstützt wird. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Mindestens eine Konstruktorüberladung mit nicht arraybasierten Params-Auflistungsparametern kann nur in erweiterter Form anwendbar sein, was während der dynamischen Verteilung nicht unterstützt wird. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Eine oder mehrere Indexerüberladungen mit nicht arraybasierten Params-Auflistungsparametern sind möglicherweise nur in erweiterter Form anwendbar, was während der dynamischen Verteilung nicht unterstützt wird. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Eine oder mehrere Indexerüberladungen mit nicht arraybasierten Params-Auflistungsparametern sind möglicherweise nur in erweiterter Form anwendbar, was während der dynamischen Verteilung nicht unterstützt wird. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Eine oder mehrere Überladungen der Methode "{0}" mit nicht arraybasierten Params-Auflistungsparametern sind möglicherweise nur in erweiterter Form anwendbar, was während der dynamischen Verteilung nicht unterstützt wird. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Eine oder mehrere Überladungen der Methode mit nicht arraybasierten Params-Auflistungsparametern sind möglicherweise nur in erweiterter Form anwendbar, was während der dynamischen Verteilung nicht unterstützt wird. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - Non-Nullable-{0} "{1}" muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie eine Deklaration von "{0}" als Nullable. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + Non-Nullable-{0} "{1}" muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie eine Deklaration von "{0}" als Nullable. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Ein Non-Nullable-Feld muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Erwägen Sie die Deklaration als Nullable. @@ -10963,7 +10963,7 @@ Sie sollten das Unterdrücken der Warnung nur in Betracht ziehen, wenn Sie siche Cannot specify a default value for a parameter collection - Es kann kein Standardwert für ein Parameterarray angegeben werden. + Für eine Parameterauflistung kann kein Standardwert angegeben werden diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 74a97dcc504cf..700e8f5912984 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + No se puede usar una instrucción de bloqueo en un valor de tipo 'System.Threading.Lock' en métodos asincrónicos ni expresiones lambda asincrónicas. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Los argumentos de tipo para el método "{0}" no se pueden inferir del uso porque se usa un argumento con tipo dinámico y el método tiene un parámetro de colección params no matriz. Intente especificar los argumentos de tipo explícitamente. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + El tipo de expresión de colección "{0}" debe tener una instancia o un método de extensión "Add" al que se pueda llamar con un único argumento. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + El tipo de expresión de colección debe tener un constructor aplicable al que se pueda llamar sin argumentos. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + El destino de la expresión de colección '{0}' no tiene tipo de elemento. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + No use "System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute". Use la palabra clave "params" en su lugar. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Los modificadores no se pueden colocar en declaración "using" @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Ambigüedad entre las formas expandidas y normales del parámetro de colección params no matriz de "{0}", el único argumento correspondiente tiene el tipo "dynamic". Considere la posibilidad de convertir el argumento dinámico. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + El constructor "{0}" deja el miembro requerido "{1}" sin inicializar. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Un árbol de expresión no puede contener una forma expandida de parámetro de colección params no matriz. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + "{0}" no contiene una definición para un método "Add" de instancia adecuado Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + La creación de la colección params "{0}" da como resultado una cadena infinita de invocación del constructor "{1}". Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + El tipo de colección params no matriz que debe tener un constructor aplicable al que se pueda llamar sin argumentos. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + El método "{0}" no puede ser menos visible que el miembro con la colección params "{1}". The params parameter must have a valid collection type - The params parameter must have a valid collection type + El parámetro params debe tener un tipo de colección válido @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + inicializador de indizador implícito @@ -2304,7 +2304,7 @@ Lock object - Lock object + Bloquear objeto @@ -2319,7 +2319,7 @@ params collections - params collections + colecciones params @@ -2369,7 +2369,7 @@ string escape character - string escape character + carácter de escape de cadena @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Un valor de tipo 'System.Threading.Lock' convertido a otro tipo probablemente usará un bloqueo basado en monitor no deseado en la instrucción 'lock' . A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Un valor de tipo 'System.Threading.Lock' convertido a otro tipo probablemente usará un bloqueo basado en monitor no deseado en la instrucción 'lock' . @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Una o más sobrecargas del constructor que tienen un parámetro de colección params no matriz que podrían ser aplicables solo en forma expandida, lo que no se admite durante el envío dinámico. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Una o más sobrecargas del constructor que tienen un parámetro de colección params no matriz que podrían ser aplicables solo en forma expandida, lo que no se admite durante el envío dinámico. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Una o más sobrecargas del indexador que tienen un parámetro de colección params no matriz que podrían ser aplicables solo en forma expandida, lo que no se admite durante el envío dinámico. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Una o más sobrecargas del indexador que tienen un parámetro de colección params no matriz que podrían ser aplicables solo en forma expandida, lo que no se admite durante el envío dinámico. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Una o varias sobrecargas del método "{0}" que tienen un parámetro de colección params no matriz que podrían ser aplicables solo en forma expandida, lo que no se admite durante el envío dinámico. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Una o varias sobrecargas del método que tienen un parámetro de colección params no matriz que podrían ser aplicables solo en forma expandida, lo que no se admite durante el envío dinámico. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - El elemento {0} "{1}" que no acepta valores NULL debe contener un valor distinto de NULL al salir del constructor. Considere la posibilidad de declarar el elemento {0} como que admite un valor NULL. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + El elemento {0} "{1}" que no acepta valores NULL debe contener un valor distinto de NULL al salir del constructor. Considere la posibilidad de declarar el elemento {0} como que admite un valor NULL. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Un campo que no acepta valores NULL debe contener un valor distinto de NULL al salir del constructor. Considere la posibilidad de declararlo como que admite un valor NULL. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Un campo que no acepta valores NULL debe contener un valor distinto de NULL al salir del constructor. Considere la posibilidad de declararlo como que admite un valor NULL. @@ -10963,7 +10963,7 @@ Considere la posibilidad de suprimir la advertencia solo si tiene la seguridad d Cannot specify a default value for a parameter collection - No se puede especificar un valor predeterminado para una matriz de parámetros + No se puede especificar un valor predeterminado para una colección de parámetros diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 1889230abd22d..b5d7cacbfdb09 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Vous ne pouvez pas utiliser une instruction lock sur une valeur de type « System.Threading.Lock » dans des méthodes asynchrones ou des expressions lambda asynchrones. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Désolé... Nous ne pouvons pas déduire les arguments de type pour la méthode « {0} » à partir de l’utilisation, car un argument avec un type dynamique est utilisé et la méthode a un paramètre de collection de params non-tableau. Essayez de spécifier explicitement les arguments de type. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Le type d’expression de collection « {0} » doit avoir une instance ou une méthode d’extension « Ajouter » qui peut être appelée avec un seul argument. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Le type d’expression de collection doit avoir un constructeur applicable que vous pouvez appeler sans argument. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + La cible « {0} » de l’expression de collection n’a aucun type d’élément. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + N’utilisez pas « System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute ». Utilisez plutôt le mot clé « params ». @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Les modificateurs ne peuvent pas être placés en utilisant des déclarations @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Il existe une ambiguïté entre les formes développées et normales d’un paramètre de collection de params non-tableau de « {0} », le seul argument correspondant est de type « dynamique ». Envisagez d’effectuer un cast de l’argument dynamique. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Le constructeur « {0} » laisse le membre « {1} » requis non initialisé. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Une arborescence d’expression ne peut pas contenir une forme développée de paramètre de collection de params non-tableau. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + « {0} » ne contient pas de définition pour une méthode « Add » d’instance appropriée Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + La création de la collection de params « {0} » entraîne une chaîne infinie d’invocation du constructeur « {1} ». Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Le type de collection de params non-tableau doit avoir un constructeur applicable que vous pouvez appeler sans argument. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + La méthode « {0} » peut pas être moins visible que le membre avec la collection de params « {1} ». The params parameter must have a valid collection type - The params parameter must have a valid collection type + Le paramètre params doit avoir un type de collection valide @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + initialiseur d’indexeur implicite @@ -2304,7 +2304,7 @@ Lock object - Lock object + Verrouiller des objets @@ -2319,7 +2319,7 @@ params collections - params collections + collections de params @@ -2369,7 +2369,7 @@ string escape character - string escape character + caractère d’échappement de chaîne @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Une valeur de type « System.Threading.Lock » convertie en un autre type va probablement utiliser un verrouillage inattendu basé sur un moniteur dans l’instruction « lock ». A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Une valeur de type « System.Threading.Lock » convertie en un autre type va probablement utiliser un verrouillage inattendu basé sur un moniteur dans l’instruction « lock ». @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Il est possible qu’une ou plusieurs surcharges de constructeur ayant un paramètre de collection de params non-tableau soient applicables uniquement sous une forme développée, ce qui n’est pas pris en charge lors d’une répartition dynamique. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Il est possible qu’une ou plusieurs surcharges de constructeur ayant un paramètre de collection de params non-tableau soient applicables uniquement sous une forme développée, ce qui n’est pas pris en charge lors d’une répartition dynamique. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Il est possible qu’une ou plusieurs surcharges d’indexeur ayant un paramètre de collection de params non-tableau soient applicables uniquement sous une forme développée, ce qui n’est pas pris en charge lors d’une répartition dynamique. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Il est possible qu’une ou plusieurs surcharges d’indexeur ayant un paramètre de collection de params non-tableau soient applicables uniquement sous une forme développée, ce qui n’est pas pris en charge lors d’une répartition dynamique. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Il est possible qu’une ou plusieurs surcharges de méthode « {0} » ayant un paramètre de collection de params non-tableau soient applicables uniquement sous une forme développée, ce qui n’est pas pris en charge lors d’une répartition dynamique. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Il est possible qu’une ou plusieurs surcharges de méthode ayant un paramètre de collection de params non-tableau soient applicables uniquement sous une forme développée, ce qui n’est pas pris en charge lors d’une répartition dynamique. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - Le {0} '{1}' non-nullable doit contenir une valeur non-null lors de la fermeture du constructeur. Envisagez de déclarer le {0} comme nullable. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + Le {0} '{1}' non-nullable doit contenir une valeur non-null lors de la fermeture du constructeur. Envisagez de déclarer le {0} comme nullable. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Un champ non-nullable doit contenir une valeur non-null lors de la fermeture du constructeur. Envisagez de déclarer le champ comme nullable. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Un champ non-nullable doit contenir une valeur non-null lors de la fermeture du constructeur. Envisagez de déclarer le champ comme nullable. @@ -10963,7 +10963,7 @@ Supprimez l'avertissement seulement si vous êtes sûr de ne pas vouloir attendr Cannot specify a default value for a parameter collection - Impossible de spécifier une valeur par défaut pour un tableau de paramètres + Désolé... Nous ne pouvons pas spécifier une valeur par défaut pour une collection de paramètres diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 6d08108ae813a..920818aa35c52 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Non è possibile usare un'istruzione lock in un valore di tipo 'System.Threading.Lock' nei metodi asincroni o nelle espressioni lambda asincrone. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Impossibile dedurre dall'utilizzo gli argomenti di tipo generico per il metodo '{0}' poiché è utilizzato un argomento con tipo dinamico e il metodo include un parametro di raccolta dei parametri non di matrice. Provare a specificare gli argomenti di tipo generico in modo esplicito. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Il tipo di espressione della raccolta "{0}" deve avere un'istanza o un metodo di estensione "Add" da poter chiamare con un argomento singolo. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Il tipo di espressione della raccolta deve avere un costruttore applicabile che può essere chiamato senza argomenti. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + La destinazione dell'espressione di raccolta '{0}' non contiene alcun tipo di elemento. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Non usare 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Al suo posto, usare la parola chiave 'params'. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Non è possibile inserire modificatori nelle dichiarazioni using @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Ambiguità tra forme espanse e normali del parametro di raccolta parametri non di matrice di '{0}'. L'unico argomento corrispondente ha il tipo 'dynamic'. Provare a eseguire il cast dell'argomento dinamico. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Il costruttore '{0}' lascia non inizializzato il membro richiesto '{1}'. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Un albero delle espressioni non può contenere un formato espanso di parametro di raccolta dei parametri non di matrice. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + '{0}' non contiene una definizione per un metodo 'Add' di istanza appropriato Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + La creazione della raccolta di parametri '{0}' comporta una catena infinita di chiamate del costruttore '{1}'. Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Il tipo di raccolta dei parametri non di matrice deve avere un costruttore applicabile che può essere chiamato senza argomenti. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + Il metodo '{0}' non può essere meno visibile del membro con raccolta parametri '{1}'. The params parameter must have a valid collection type - The params parameter must have a valid collection type + Il parametro params deve avere un tipo di raccolta valido @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + inizializzatore indicizzatore implicito @@ -2304,7 +2304,7 @@ Lock object - Lock object + Blocca oggetto @@ -2319,7 +2319,7 @@ params collections - params collections + raccolte parametri @@ -2369,7 +2369,7 @@ string escape character - string escape character + carattere di escape stringa @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Un valore di tipo 'System.Threading.Lock' convertito in un tipo diverso userà probabilmente un blocco basato su monitoraggio non intenzionale nell'istruzione 'lock'. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Un valore di tipo 'System.Threading.Lock' convertito in un tipo diverso userà probabilmente un blocco basato su monitoraggio non intenzionale nell'istruzione 'lock'. @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uno o più overload del costruttore con parametro di raccolta dei parametri non di matrice possono essere applicati solo in formato espanso che non è supportato durante l'invio dinamico. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uno o più overload del costruttore con parametro di raccolta dei parametri non di matrice possono essere applicati solo in formato espanso che non è supportato durante l'invio dinamico. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uno o più overload dell'indicizzatore con parametro di raccolta dei parametri non di matrice possono essere applicati solo in formato espanso che non è supportato durante l'invio dinamico. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uno o più overload dell'indicizzatore con parametro di raccolta dei parametri non di matrice possono essere applicati solo in formato espanso che non è supportato durante l'invio dinamico. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uno o più overload del metodo '{0}' con parametro di raccolta dei parametri non di matrice possono essere applicati solo in formato espanso che non è supportato durante l'invio dinamico. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uno o più overload del metodo con parametro di raccolta dei parametri non di matrice possono essere applicati solo in formato espanso che non è supportato durante l'invio dinamico. @@ -5084,13 +5084,13 @@ target:module Compila un modulo che può essere aggiunto ad altro - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - L'elemento {0} '{1}' non nullable deve contenere un valore non Null all'uscita dal costruttore. Provare a dichiarare {0} come nullable. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + L'elemento {0} '{1}' non nullable deve contenere un valore non Null all'uscita dal costruttore. Provare a dichiarare {0} come nullable. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Il campo non nullable deve contenere un valore non Null all'uscita dal costruttore. Provare a dichiararlo come nullable. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Il campo non nullable deve contenere un valore non Null all'uscita dal costruttore. Provare a dichiararlo come nullable. @@ -10963,7 +10963,7 @@ Come procedura consigliata, è consigliabile attendere sempre la chiamata. Cannot specify a default value for a parameter collection - Impossibile specificare un valore predefinito per una matrice di parametri + Impossibile specificare un valore predefinito per una raccolta di parametri diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 35540a24dd000..9ccc403201939 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + 型 'System.Threading.Lock' の値に対する lock ステートメントは、非同期メソッドまたは非同期ラムダ式では使用できません。 @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + メソッド '{0}' の型引数を使用法から推論できません。これは動的型のある引数が使用され、メソッドに配列以外の params コレクション パラメーターがあるためです。型引数を明示的に指定してみてください。 @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + コレクション式の型 '{0}' には、1 つの引数で呼び出すことができるインスタンスメソッドまたは拡張メソッド 'Add' が必要です。 Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + コレクション式の型には、引数なしで呼び出すことができる適用可能なコンストラクターが必要です。 @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + コレクション式のターゲット '{0}' に要素型がありません。 @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute' は使用しないでください。代わりに 'params' キーワードを使用してください。 @@ -989,7 +989,7 @@ Method '{0}' must be non-generic to match '{1}'. - '{1}' に一致するには、メソッド '{0}' は非ジェネリックである必要があります。 + '{0}' に一致するには、メソッド '{1}' は非ジェネリックである必要があります。 @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + 宣言を使用して修飾子を配置することはできません @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + '{0}' の非配列 params コレクション パラメーターの展開形式と通常の形式の間のあいまいさです。対応する引数の型は 'dynamic' のみです。動的引数のキャストを検討してください。 Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + コンストラクター '{0}' は、必要なメンバー '{1}' を初期化しないままにします。 An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + 式ツリーに、配列以外の params コレクション パラメーターの展開形式を含めないようにしてください。 '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + '{0}' には、適切なインスタンス 'Add' メソッドの定義が含まれていません Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + パラメーター コレクション '{0}' を作成すると、コンストラクター '{1}' の呼び出しのチェーンが無限になります。 Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + 配列 params コレクション以外の種類には、引数なしで呼び出すことができる適用可能なコンストラクターが必要です。 Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + メソッド '{0}' は params コレクション '{1}' を持つメンバーより低く表示できません。 The params parameter must have a valid collection type - The params parameter must have a valid collection type + params パラメーターには有効なコレクションの種類が必要です @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + 暗黙的なインデクサー初期化子 @@ -2304,7 +2304,7 @@ Lock object - Lock object + オブジェクトをロックする @@ -2319,7 +2319,7 @@ params collections - params collections + params コレクション @@ -2369,7 +2369,7 @@ string escape character - string escape character + 文字列エスケープ文字 @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 型 'System.Threading.Lock' の値が別の型に変換されると、'lock' ステートメントで意図しない可能性の高いモニターベースのロックが使用されます。 A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 型 'System.Threading.Lock' の値が別の型に変換されると、'lock' ステートメントで意図しない可能性の高いモニターベースのロックが使用されます。 @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 配列 params 以外のコレクション パラメーターを持つ 1 つ以上のコンストラクター オーバーロードは、動的ディスパッチ中にサポートされない拡張形式でのみ適用できます。 One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 配列 params 以外のコレクション パラメーターを持つ 1 つ以上のコンストラクター オーバーロードは、動的ディスパッチ中にサポートされない拡張形式でのみ適用できます。 One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 配列パラメーター以外のコレクション パラメーターを持つ 1 つ以上のインデクサー オーバーロードは、動的ディスパッチ中にサポートされない拡張形式でのみ適用できます。 One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 配列パラメーター以外のコレクション パラメーターを持つ 1 つ以上のインデクサー オーバーロードは、動的ディスパッチ中にサポートされない拡張形式でのみ適用できます。 One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 配列 params 以外のコレクション パラメーターを持つメソッド '{0}' の 1 つ以上のオーバーロードは、動的ディスパッチ中にサポートされない拡張形式でのみ適用できます。 One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 配列パラメーター以外のコレクション パラメーターを持つメソッドの 1 つ以上のオーバーロードは、動的ディスパッチ中にサポートされない拡張形式でのみ適用できます。 @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - null 非許容の {0} '{1}' には、コンストラクターの終了時に null 以外の値が入っていなければなりません。{0} を Null 許容として宣言することをご検討ください。 + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + null 非許容の {0} '{1}' には、コンストラクターの終了時に null 以外の値が入っていなければなりません。{0} を Null 許容として宣言することをご検討ください。 - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - null 非許容のフィールドには、コンストラクターの終了時に null 以外の値が入っていなければなりません。Null 許容として宣言することをご検討ください。 + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + null 非許容のフィールドには、コンストラクターの終了時に null 以外の値が入っていなければなりません。Null 許容として宣言することをご検討ください。 @@ -10963,7 +10963,7 @@ You should consider suppressing the warning only if you're sure that you don't w Cannot specify a default value for a parameter collection - パラメーター配列には既定値を指定できません + パラメーター コレクションには、既定値を指定できません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 84181d36d88a3..736c1e7ed0f2c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + 'System.Threading.Lock' 형식의 값에 대한 lock 문은 비동기 메서드 또는 비동기 람다 식에서 사용할 수 없습니다. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + 동적 형식을 가진 인수가 사용되고 메서드에 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있으므로 메서드 '{0}' 형식 인수를 사용법에서 유추할 수 없습니다. 형식 인수를 명시적으로 지정해 보세요. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + 컬렉션 식 형식 '{0}'에는 단일 인수로 호출할 수 있는 인스턴스 또는 확장 메서드 'Add'가 있어야 합니다. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + 컬렉션 식 형식에는 인수 없이 호출할 수 있는 적용 가능한 생성자가 있어야 합니다. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + 컬렉션 식 대상 '{0}'에 요소 형식이 없습니다. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'를 사용하지 마세요. 대신 'params' 키워드를 사용합니다. @@ -989,7 +989,7 @@ Method '{0}' must be non-generic to match '{1}'. - '{1}' 일치하려면 메서드 '{0}' 제네릭이 아니어야 합니다. + '{0}' 메서드는 '{1}'에 일치하려면 제네릭이 아니어야 합니다. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + 선언을 사용하여 한정자를 배치할 수 없습니다. @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + '{0}'의 배열 매개 변수가 아닌 params 컬렉션 매개 변수의 확장된 형식과 일반 형식 간의 모호성입니다. 해당 인수에는 'dynamic' 형식만 있습니다. 동적 인수를 캐스팅하는 것이 좋습니다. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + 생성자 '{0}'은(는) 필수 멤버 '{1}'을(를) 초기화되지 않은 상태로 둡니다. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + 식 트리에는 배열 매개 변수가 아닌 params 컬렉션 매개 변수의 확장된 형식을 포함할 수 없습니다. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + '{0}'에 적합한 인스턴스 'Add' 메서드에 대한 정의가 포함되어 있지 않습니다. Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + params 컬렉션 '{0}'을(를) 만들면 생성자 '{1}’이(가) 무한 체인으로 호출됩니다. Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + 배열 매개 변수가 아닌 params 컬렉션 형식에는 인수 없이 호출할 수 있는 적용 가능한 생성자가 있어야 합니다. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + '{0}' 메서드는 params 컬렉션이 '{1}'인 멤버보다 작게 표시할 수 없습니다. The params parameter must have a valid collection type - The params parameter must have a valid collection type + params 매개 변수는 유효한 컬렉션 형식이어야 합니다. @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + 암시적 인덱서 이니셜라이저 @@ -2304,7 +2304,7 @@ Lock object - Lock object + 개체 잠금 @@ -2319,7 +2319,7 @@ params collections - params collections + params 컬렉션 @@ -2369,7 +2369,7 @@ string escape character - string escape character + 문자열 이스케이프 문자 @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 다른 형식으로 변환된 'System.Threading.Lock' 형식의 값은 ‘lock’ 문에서 의도하지 않은 모니터 기반 잠금을 사용합니다. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 다른 형식으로 변환된 'System.Threading.Lock' 형식의 값은 ‘lock’ 문에서 의도하지 않은 모니터 기반 잠금을 사용합니다. @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있는 하나 이상의 생성자 오버로드는 동적 디스패치 중에 지원되지 않는 확장된 형식에서만 적용할 수 있습니다. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있는 하나 이상의 생성자 오버로드는 동적 디스패치 중에 지원되지 않는 확장된 형식에서만 적용할 수 있습니다. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있는 하나 이상의 인덱서 오버로드는 동적 디스패치 중에 지원되지 않는 확장된 형식에서만 적용할 수 있습니다. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있는 하나 이상의 인덱서 오버로드는 동적 디스패치 중에 지원되지 않는 확장된 형식에서만 적용할 수 있습니다. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있는 '{0}' 메서드의 하나 이상의 오버로드는 동적 디스패치 중에 지원되지 않는 확장된 형식에서만 적용할 수 있습니다. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 배열 매개 변수가 아닌 params 컬렉션 매개 변수가 있는 메서드의 오버로드 하나 이상은 동적 디스패치 중에 지원되지 않는 확장된 형식에서만 적용할 수 있습니다. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - 생성자를 종료할 때 null을 허용하지 않는 {0} '{1}'에 null이 아닌 값을 포함해야 합니다. {0}을(를) null 허용으로 선언해 보세요. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + 생성자를 종료할 때 null을 허용하지 않는 {0} '{1}'에 null이 아닌 값을 포함해야 합니다. {0}을(를) null 허용으로 선언해 보세요. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - 생성자를 종료할 때 null을 허용하지 않는 필드에 null이 아닌 값을 포함해야 합니다. null 허용으로 선언해 보세요. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + 생성자를 종료할 때 null을 허용하지 않는 필드에 null이 아닌 값을 포함해야 합니다. null 허용으로 선언해 보세요. @@ -10963,7 +10963,7 @@ You should consider suppressing the warning only if you're sure that you don't w Cannot specify a default value for a parameter collection - 매개 변수 배열의 기본값을 지정할 수 없습니다. + 매개 변수 컬렉션의 기본값을 지정할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c9ff6a2bb9ca6..e05459fae3648 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Instrukcja blokady na wartości typu „System.Threading.Lock” nie może być używana w metodach asynchronicznych lub asynchronicznych wyrażeniach lambda. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Argumenty typu dla metody „{0}” nie mogą być wywnioskowane z użycia, ponieważ używany jest argument typu dynamicznego, a metoda ma parametr kolekcji params inny niż tablica. Spróbuj jawnie określić argumenty typu. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Typ wyrażenia kolekcji „{0}” musi mieć wystąpienie lub metodę rozszerzenia „Dodaj”, którą można wywołać z pojedynczym argumentem. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Typ wyrażenia kolekcji musi mieć odpowiedni konstruktor, który można wywołać bez argumentów. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + Wyrażenie kolekcji docelowej „{0}” nie ma typu elementu. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Nie używaj atrybutu „System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute”. Zamiast niego użyj słowa kluczowego „params”. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Modyfikatorów nie można umieszczać przy użyciu deklaracji usług @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Niejednoznaczność między rozszerzoną i normalną formą parametru kolekcji params niebędącego tablicą „{0}”, jedyny odpowiadający argument ma typ dynamiczny. Rozważ rzutowanie argumentu dynamicznego. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Konstruktor „{0}” pozostawia wymaganą składową "{1}". An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Drzewo wyrażeń nie może zawierać rozszerzonej formy parametru kolekcji params innego niż tablica. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + Element „{0}” nie zawiera definicji odpowiedniej metody „Dodaj” wystąpienia Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Utworzenie kolekcji parametrów „{0}” powoduje nieskończony łańcuch wywołania konstruktora „{1}”. Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Typ kolekcji niebędący tablicą parametrów musi mieć odpowiedni konstruktor, który można wywołać bez argumentów. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + Metoda „{0}” nie może być mniej widoczna niż składowa z kolekcją parametrów „{1}”. The params parameter must have a valid collection type - The params parameter must have a valid collection type + Parametr params musi mieć prawidłowy typ kolekcji @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + niejawny inicjator indeksatora @@ -2304,7 +2304,7 @@ Lock object - Lock object + Zablokuj obiekt @@ -2319,7 +2319,7 @@ params collections - params collections + kolekcje params @@ -2369,7 +2369,7 @@ string escape character - string escape character + ciąg — znak ucieczki @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Wartość typu „System.Threading.Lock” przekonwertowana na inny typ użyje prawdopodobnie niezamierzonego blokowania opartego na monitorze w instrukcji „zablokuj”. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Wartość typu „System.Threading.Lock” przekonwertowana na inny typ użyje prawdopodobnie niezamierzonego blokowania opartego na monitorze w instrukcji „zablokuj”. @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno lub więcej przeciążeń konstruktora z parametrem kolekcji parametrów niebędącym tablicą może mieć zastosowanie tylko w rozszerzonej formie, która nie jest obsługiwana podczas dynamicznego wysyłania. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno lub więcej przeciążeń konstruktora z parametrem kolekcji parametrów niebędącym tablicą może mieć zastosowanie tylko w rozszerzonej formie, która nie jest obsługiwana podczas dynamicznego wysyłania. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno lub więcej przeciążeń indeksatora z parametrem kolekcji niebędącym tablicą parametrów może mieć zastosowanie tylko w rozszerzonej formie, która nie jest obsługiwana podczas dynamicznego wysyłania. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Jedno lub więcej przeciążeń indeksatora z parametrem kolekcji niebędącym tablicą parametrów może mieć zastosowanie tylko w rozszerzonej formie, która nie jest obsługiwana podczas dynamicznego wysyłania. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Co najmniej jedno przeciążenie metody „{0}” z parametrem kolekcji params innych niż tablicowe może mieć zastosowanie tylko w rozszerzonej formie, która nie jest obsługiwana podczas dynamicznego wysyłania. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Co najmniej jedno przeciążenie metody posiadającej parametr kolekcji params inny niż tablica może mieć zastosowanie tylko w rozszerzonej formie, która nie jest obsługiwana podczas dynamicznego wysyłania. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - Niedopuszczający wartości null element {0} „{1}” musi zawierać wartość inną niż null podczas kończenia działania konstruktora. Rozważ zadeklarowanie elementu {0} jako dopuszczającego wartość null. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + Niedopuszczający wartości null element {0} „{1}” musi zawierać wartość inną niż null podczas kończenia działania konstruktora. Rozważ zadeklarowanie elementu {0} jako dopuszczającego wartość null. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Pole niedopuszczające wartości null musi zawierać wartość inną niż null podczas kończenia działania konstruktora. Rozważ zadeklarowanie pola jako dopuszczającego wartość null. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Pole niedopuszczające wartości null musi zawierać wartość inną niż null podczas kończenia działania konstruktora. Rozważ zadeklarowanie pola jako dopuszczającego wartość null. @@ -10963,7 +10963,7 @@ Pominięcie ostrzeżenia należy wziąć pod uwagę tylko w sytuacji, gdy na pew Cannot specify a default value for a parameter collection - Nie można określić wartości domyślnej dla tablicy parametrów + Nie można określić wartości domyślnej dla tablicy parametrów diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 8d098c835cfaa..528dfbfdf5076 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Uma instrução lock em um valor do tipo "System.Threading.Lock" não pode ser usada em métodos assíncronos ou em expressões lambda assíncronas. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Os argumentos de tipo para o método '{0}' não podem ser inferidos a partir do uso porque um argumento com tipo dinâmico é usado e o método tem um parâmetro de coleção de params que não é uma matriz. Tente especificar explicitamente os argumentos de tipo. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + O tipo de expressão de coleção '{0}' deve ter uma instância ou método de extensão 'Add' que pode ser chamado com apenas um argumento. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + O tipo de expressão de coleção precisa ter um construtor aplicável que possa ser chamado sem argumentos. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + O destino da expressão de coleção "{0}" não tem nenhum tipo de elemento. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Não use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Em vez disso, use a palavra-chave 'params'. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Os modificadores não podem ser colocados em declarações de uso @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Ambiguidade entre as formas expandida e normal do parâmetro de coleção de params que não é uma matriz de '{0}', o único argumento correspondente tem o tipo 'dynamic'. Considere a conversão do argumento dinâmico. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + O construtor '{0}' deixa o membro obrigatório '{1}' não inicializado. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Uma árvore de expressão não pode conter uma forma expandida de parâmetro de coleção de params que não seja uma matriz. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + '{0}' não contém uma definição para um método 'Add' de instância adequado Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + A criação da coleção de params '{0}' resulta em uma cadeia infinita de invocação do construtor '{1}'. Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Tipos de coleção de params que não sejam matrizes devem ter um construtor aplicável que possa ser chamado sem argumentos. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + O método '{0}' não pode ter uma visibilidade menor que o membro com a coleção de params '{1}'. The params parameter must have a valid collection type - The params parameter must have a valid collection type + O parâmetro params deve ter um tipo de coleção válido @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + inicializador de indexador implícito @@ -2304,7 +2304,7 @@ Lock object - Lock object + Bloquear objeto @@ -2319,7 +2319,7 @@ params collections - params collections + coleções de params @@ -2369,7 +2369,7 @@ string escape character - string escape character + cadeia de caracteres de escape @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Um valor do tipo "System.Threading.Lock" convertido em um tipo diferente usará o bloqueio baseado em monitor provavelmente não intencional na instrução "lock". A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Um valor do tipo "System.Threading.Lock" convertido em um tipo diferente usará o bloqueio baseado em monitor provavelmente não intencional na instrução "lock". @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uma ou mais sobrecargas de construtor com parâmetro de coleção de params que não é uma matriz podem ser aplicáveis somente na forma expandida, o que não tem suporte durante o envio dinâmico. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uma ou mais sobrecargas de construtor com parâmetro de coleção de params que não é uma matriz podem ser aplicáveis somente na forma expandida, o que não tem suporte durante o envio dinâmico. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uma ou mais sobrecargas de indexador com parâmetro de coleção de params que não é uma matriz podem ser aplicáveis somente na forma expandida, o que não tem suporte durante o envio dinâmico. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uma ou mais sobrecargas de indexador com parâmetro de coleção de params que não é uma matriz podem ser aplicáveis somente na forma expandida, o que não tem suporte durante o envio dinâmico. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Um ou mais métodos sobrecarregados '{0}' com parâmetro de coleção de params que não é uma matriz podem ser aplicáveis apenas em forma expandida, o que não é suportado durante o despacho dinâmico. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Uma ou mais sobrecargas de método com parâmetro de coleção de params que não é uma matriz podem ser aplicáveis apenas na forma expandida, o que não tem suporte durante a expedição dinâmica. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - O {0} não anulável '{1}' precisa conter um valor não nulo ao sair do construtor. Considere declarar o {0} como anulável. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + O {0} não anulável '{1}' precisa conter um valor não nulo ao sair do construtor. Considere declarar o {0} como anulável. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - O campo não anulável precisa conter um valor não nulo ao sair do construtor. Considere declará-lo como anulável. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + O campo não anulável precisa conter um valor não nulo ao sair do construtor. Considere declará-lo como anulável. @@ -10963,7 +10963,7 @@ Você pode suprimir o aviso se tiver certeza de que não vai querer aguardar a c Cannot specify a default value for a parameter collection - Não é possível especificar um valor padrão para uma matriz de parâmetros + Não é possível especificar um valor padrão para uma coleção de parâmetros diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index c0408330befc1..14c4a7c5ce8fc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + Оператор блокировки над значением типа "System.Threading.Lock" нельзя использовать в асинхронных методах и в асинхронных лямбда-выражениях. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Аргументы типа для метода "{0}" не могут быть выведены из использования, поскольку используется аргумент с динамическим типом и метод имеет параметр коллекции params, не являющийся массивом. Попробуйте явно указать аргументы типа. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + Тип выражения коллекции "{0}" должен иметь экземпляр или метод расширения "Add", который можно вызвать с помощью одного аргумента. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Тип выражения коллекции должен иметь применимый конструктор, который может быть вызван без аргументов. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + У целевого объекта выражения "{0}" нет типа элемента. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Не используйте System.ParamArrayAttribute/System.Runtime.CompilerServices.ParamCollectionAttribute. Используйте ключевое слово "params". @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Модификаторы нельзя разместить при использовании объявлений. @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + Неоднозначность между расширенной и нормальной формами параметра коллекции params, отличных от массива, "{0}", единственный соответствующий аргумент имеет тип "dynamic". Рассмотрите возможность использования динамического аргумента. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Конструктор "{0}" оставляет необходимый элемент "{1}" не инициализированным. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + Дерево выражений не может содержать расширенную форму параметра коллекции params, не являющегося массивом. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + "{0}" не содержит определения для подходящего экземпляра метода "Add" Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Создание коллекции params "{0}" приводит к бесконечной цепочке вызовов конструктора "{1}". Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Тип коллекции params, не являющийся массивом, должен иметь применимый конструктор, который можно вызывать без аргументов. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + Метод "{0}" не может быть менее видимым, чем элемент с коллекцией параметров "{1}". The params parameter must have a valid collection type - The params parameter must have a valid collection type + Параметр params должен иметь допустимый тип коллекции. @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + неявный инициализатор индексатора @@ -2304,7 +2304,7 @@ Lock object - Lock object + Блокировать объект @@ -2319,7 +2319,7 @@ params collections - params collections + коллекции params @@ -2369,7 +2369,7 @@ string escape character - string escape character + escape-символ строки @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Значение типа "System.Threading.Lock", преобразованное в другой тип, будет использовать (скорее всего, непреднамеренную) блокировку на основе монитора в инструкции "lock". A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Значение типа "System.Threading.Lock", преобразованное в другой тип, будет использовать (скорее всего, непреднамеренную) блокировку на основе монитора в инструкции "lock". @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Одна или несколько перегрузок конструктора, содержащих параметр коллекции params, отличный от массива, могут быть применимы только в расширенной форме, не поддерживаемой во время динамической отправки. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Одна или несколько перегрузок конструктора, содержащих параметр коллекции params, отличный от массива, могут быть применимы только в расширенной форме, не поддерживаемой во время динамической отправки. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Одна или несколько перегрузок индексатора, содержащих параметр коллекции params, отличный от массива, могут быть применимы только в расширенной форме, не поддерживаемой во время динамической отправки. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Одна или несколько перегрузок индексатора, содержащих параметр коллекции params, отличный от массива, могут быть применимы только в расширенной форме, не поддерживаемой во время динамической отправки. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Одна или несколько перегрузок метода "{0}", имеющих параметр коллекции params, отличный от массива, могут быть применимы только в расширенной форме, которая не поддерживается во время динамической отправки. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Одна или несколько перегрузок метода, имеющих параметр коллекции params, отличный от массива, могут быть применимы только в расширенной форме, которая не поддерживается во время динамической отправки. @@ -5085,13 +5085,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - {0} "{1}", не допускающий значения NULL, должен содержать значение, отличное от NULL, при выходе из конструктора. Возможно, стоит объявить {0} как допускающий значения NULL. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + {0} "{1}", не допускающий значения NULL, должен содержать значение, отличное от NULL, при выходе из конструктора. Возможно, стоит объявить {0} как допускающий значения NULL. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Поле, не допускающее значения NULL, должно содержать значение, отличное от NULL, при выходе из конструктора. Возможно, стоит объявить поле как допускающее значения NULL. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Поле, не допускающее значения NULL, должно содержать значение, отличное от NULL, при выходе из конструктора. Возможно, стоит объявить поле как допускающее значения NULL. @@ -10964,7 +10964,7 @@ You should consider suppressing the warning only if you're sure that you don't w Cannot specify a default value for a parameter collection - Не удалось указать значение по умолчанию для массива параметров. + Не удается указать значение по умолчанию для коллекции параметров. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5af1205aab436..45e26b5b7c14f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + 'System.Threading.Lock' türündeki bir değere ilişkin lock deyimi, asenkron yöntemlerde veya asenkron lambda ifadelerinde kullanılamaz. @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + Dinamik türe sahip bir bağımsız değişken kullanıldığından ve metot dizi olmayan params koleksiyon parametresine sahip olduğundan, '{0}' metodu için tür bağımsız değişkeni kullanımdan çıkarsanamıyor. Tür bağımsız değişkenlerini açıkça belirtmeyi deneyin. @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + '{0}' koleksiyon ifade türünün tek bir bağımsız değişken ile çağrılabilecek bir örnek veya 'Add' genişletme yöntemi içermesi gerekir. Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + Koleksiyon ifade türünün, bağımsız değişken olmadan çağrılabilecek geçerli bir oluşturucusu olmalıdır. @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + '{0}' koleksiyon ifadesi hedefinin öğe türü yok. @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute' kullanmayın. Bunun yerine 'params' anahtar sözcüğünü kullanın. @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + Değiştiriciler using bildirimlerine koyulamaz @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + '{0}' parametresinin dizi olmayan params koleksiyonu parametresinin genişletilmiş ve normal biçimleri arasında belirsizlik var, karşılık gelen tek bağımsız değişken 'dynamic' türüne sahip. Dinamik bağımsız değişkenin türünü dönüştürmeyi düşünün. Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + Oluşturucu '{0}', gerekli '{1}' üyesinin başlatmasını geri alıyor. An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + İfade ağacı, dizi olmayan params koleksiyonu parametresinin genişletilmiş bir formunu içeremez. '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + '{0}', uygun bir örnek 'Add' metodu için bir tanım içermiyor Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + Params koleksiyonu '{0}' oluşturma işlemi sonsuz bir oluşturucu '{1}' çağrısı zinciriyle sonuçlanıyor. Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + Dizi olmayan params koleksiyon türünün, bağımsız değişken olmadan çağrılabilecek geçerli bir oluşturucusu olmalıdır. Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + Metot '{0}', '{1}' params koleksiyonuna sahip üyeden daha az görünür olamaz. The params parameter must have a valid collection type - The params parameter must have a valid collection type + Params parametresi geçerli bir koleksiyon türüne sahip olmalıdır @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + örtük dizin oluşturucu başlatıcısı @@ -2304,7 +2304,7 @@ Lock object - Lock object + Nesne kilitleme @@ -2319,7 +2319,7 @@ params collections - params collections + params koleksiyonları @@ -2369,7 +2369,7 @@ string escape character - string escape character + dize kaçış karakteri @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Farklı bir türe dönüştürülen 'System.Threading.Lock' türündeki bir değer, 'lock' deyiminde muhtemelen istenmeyen izleyici tabanlı kilitlemeyi kullanır. A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + Farklı bir türe dönüştürülen 'System.Threading.Lock' türündeki bir değer, 'lock' deyiminde muhtemelen istenmeyen izleyici tabanlı kilitlemeyi kullanır. @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Dizi olmayan parametreler koleksiyonu parametresine sahip bir veya daha fazla oluşturucu aşırı yükleme yalnızca genişletilmiş formda uygulanıyor olabilir. Bu, dinamik dağıtım sırasında desteklenmez. One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Dizi olmayan parametreler koleksiyonu parametresine sahip bir veya daha fazla oluşturucu aşırı yükleme yalnızca genişletilmiş formda uygulanıyor olabilir. Bu, dinamik dağıtım sırasında desteklenmez. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Dizi olmayan parametreler koleksiyonu parametresine sahip bir veya daha fazla dizin oluşturucu aşırı yükleme yalnızca genişletilmiş formda uygulanıyor olabilir. Bu, dinamik dağıtım sırasında desteklenmez. One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Dizi olmayan parametreler koleksiyonu parametresine sahip bir veya daha fazla dizin oluşturucu aşırı yükleme yalnızca genişletilmiş formda uygulanıyor olabilir. Bu, dinamik dağıtım sırasında desteklenmez. One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Dizi olmayan parametreler koleksiyonu parametresine sahip '{0}' metodunun bir veya daha fazla aşırı yüklemesi yalnızca genişletilmiş formda uygulanıyor olabilir. Bu, dinamik dağıtım sırasında desteklenmez. One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + Dizi olmayan parametreler koleksiyonu parametresine sahip metodun bir veya daha fazla aşırı yüklemesi yalnızca genişletilmiş formda uygulanıyor olabilir. Bu, dinamik dağıtım sırasında desteklenmez. @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - Null atanamaz {0} '{1}', oluşturucudan çıkış yaparken null olmayan bir değer içermelidir. {0} alanını null atanabilir olarak bildirmeyi düşünün. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + Null atanamaz {0} '{1}', oluşturucudan çıkış yaparken null olmayan bir değer içermelidir. {0} alanını null atanabilir olarak bildirmeyi düşünün. - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - Null atanamaz alan, oluşturucudan çıkış yaparken null olmayan bir değer içermelidir. Alanı null atanabilir olarak bildirmeyi düşünün. + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + Null atanamaz alan, oluşturucudan çıkış yaparken null olmayan bir değer içermelidir. Alanı null atanabilir olarak bildirmeyi düşünün. @@ -10963,7 +10963,7 @@ Yalnızca asenkron çağrının tamamlanmasını beklemek istemediğinizden ve Cannot specify a default value for a parameter collection - Parametre dizisi için varsayılan değer belirtilemez + Parametre koleksiyonu için varsayılan değer belirtilemez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 083f084732eb5..bd7eb252201c2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + 类型“System.Threading.Lock”的值的 lock 语句不能用于异步方法或异步 lambda 表达式。 @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + 无法从用法推断出方法“{0}”的类型参数,因为使用了动态类型参数,并且该方法具有非数组 params 集合参数。请尝试显式指定类型参数。 @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + 集合表达式类型“{0}”必须具有可以使用单个参数调用的实例或扩展方法 "Add"。 Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + 集合表达式类型必须具有可在不带参数的情况下调用的适用构造函数。 @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + 集合表达式目标“{0}”没有元素类型。 @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'.请改用关键字“params”。 @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + 修饰符不能放置在 using 声明上 @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + “{0}”的非数组 params 集合参数的扩展形式和正常形式之间的不明确性,唯一对应参数的类型为 “dynamic”。请考虑强制转换动态参数。 Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + 构造函数“{0}”的必需成员“{1}”未初始化。 An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + 表达式树不能包含非数组 params 集合参数的展开形式。 '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + “{0}”不包含适合的实例“Add”方法的定义 Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + 创建 params 集合“{0}”导致构造函数“{1}”调用的无限链。 Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + 非数组 params 集合类型必须拥有适用的构造函数,且该函数可以在无参数的情况下调用。 Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + 方法“{0}”的可见性不能小于具有 params 集合“{1}”的成员。 The params parameter must have a valid collection type - The params parameter must have a valid collection type + Params 参数必须具有有效的集合类型 @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + 隐式索引器初始值设定项 @@ -2304,7 +2304,7 @@ Lock object - Lock object + 锁定对象 @@ -2319,7 +2319,7 @@ params collections - params collections + Params 集合 @@ -2369,7 +2369,7 @@ string escape character - string escape character + 字符串转义字符 @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 转换为不同类型的类型“System.Threading.Lock”的值将在“lock”语句中使用可能意外的基于监视器的锁定。 A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 转换为不同类型的类型“System.Threading.Lock”的值将在“lock”语句中使用可能意外的基于监视器的锁定。 @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 具有非数组 params 集合参数的一个或多个构造函数重载可能仅适用于在动态调度期间不受支持的展开格式。 One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 具有非数组 params 集合参数的一个或多个构造函数重载可能仅适用于在动态调度期间不受支持的展开格式。 One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 具有非数组 params 集合参数的一个或多个索引器重载可能仅适用于在动态调度期间不受支持的展开格式。 One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 具有非数组 params 集合参数的一个或多个索引器重载可能仅适用于在动态调度期间不受支持的展开格式。 One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 具有非数组 params 集合参数的“{0}”方法的一个或多个方法重载可能仅适用于在动态调度期间不受支持的展开格式。 One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 具有非数组 params 集合参数的方法的一个或多个重载可能仅适用于在动态调度期间不受支持的扩展形式。 @@ -5084,13 +5084,13 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - 在退出构造函数时,不可为 null 的 {0}“{1}”必须包含非 null 值。请考虑将 {0} 声明为可以为 null。 + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + 在退出构造函数时,不可为 null 的 {0}“{1}”必须包含非 null 值。请考虑将 {0} 声明为可以为 null。 - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 @@ -10963,7 +10963,7 @@ You should consider suppressing the warning only if you're sure that you don't w Cannot specify a default value for a parameter collection - 无法为参数数组指定默认值 + 无法为参数集合指定默认值 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 57774ad95b96a..bf6487794259d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -269,7 +269,7 @@ A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + 型別 'System.Threading.Lock' 的值上的 lock 陳述式不能用在非同步方法或非同步 Lambda 運算式中。 @@ -384,7 +384,7 @@ The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. - The type arguments for method '{0}' cannot be inferred from the usage because an argument with dynamic type is used and the method has a non-array params collection parameter. Try specifying the type arguments explicitly. + 無法從使用方式推斷方法 '{0}' 的型別引數,因為使用了具有動態型別的引述,而且該方法具有非陣列參數集合參數。請嘗試明確指定型別引數。 @@ -448,13 +448,13 @@ - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. - Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type '{0}'. The best overloaded method is '{1}'. + Collection expression type '{0}' must have an instance or extension method 'Add' that can be called with a single argument. + 集合運算式類型 '{0}' 必須有可以使用單一引數來呼叫的執行個體或延伸模組方法 'Add'。 Collection expression type must have an applicable constructor that can be called with no arguments. - Collection expression type must have an applicable constructor that can be called with no arguments. + 集合運算式型別必須具有可在不帶引數的情況下呼叫的適用建構函式。 @@ -464,7 +464,7 @@ Collection expression target '{0}' has no element type. - Collection expression target '{0}' has no element type. + 集合運算式目標 '{0}' 沒有元素型別。 @@ -624,7 +624,7 @@ Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. - Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + 請勿使用 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'。請改用 'params' 關鍵字。 @@ -1424,7 +1424,7 @@ Modifiers cannot be placed on using declarations - Modifiers cannot be placed on using declarations + 修飾元不能置於 using 宣告中 @@ -1544,42 +1544,42 @@ Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. - Ambiguity between expanded and normal forms of non-array params collection parameter of '{0}', the only corresponding argument has the type 'dynamic'. Consider casting the dynamic argument. + 非陣列參數集合參數 '{0}' 的展開形式和正常形式之間存在歧義,唯一對應的引數型別為 'dynamic'。請考慮轉換動態引數。 Constructor '{0}' leaves required member '{1}' uninitialized. - Constructor '{0}' leaves required member '{1}' uninitialized. + 建構函式 '{0}' 未初始化必要的成員 '{1}'。 An expression tree may not contain an expanded form of non-array params collection parameter. - An expression tree may not contain an expanded form of non-array params collection parameter. + 運算式樹狀架構不能包含展開的非陣列參數集合參數形式。 '{0}' does not contain a definition for a suitable instance 'Add' method - '{0}' does not contain a definition for a suitable instance 'Add' method + '{0}' 不包含適當執行個體 'Add' 方法的定義 Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. - Creation of params collection '{0}' results in an infinite chain of invocation of constructor '{1}'. + 建立參數集合 '{0}' 產生無限鏈結的建構函式 '{1}' 叫用。 Non-array params collection type must have an applicable constructor that can be called with no arguments. - Non-array params collection type must have an applicable constructor that can be called with no arguments. + 非陣列參數集合型別必須具有可在不帶引數的情況下呼叫的適用建構函式。 Method '{0}' cannot be less visible than the member with params collection '{1}'. - Method '{0}' cannot be less visible than the member with params collection '{1}'. + 方法 '{0}' 不能小於具有參數集合 '{1}' 的成員。 The params parameter must have a valid collection type - The params parameter must have a valid collection type + params 參數必須具有有效的集合型別 @@ -2249,7 +2249,7 @@ implicit indexer initializer - implicit indexer initializer + 隱含索引子初始設定式 @@ -2304,7 +2304,7 @@ Lock object - Lock object + 鎖定物件 @@ -2319,7 +2319,7 @@ params collections - params collections + 參數集合 @@ -2369,7 +2369,7 @@ string escape character - string escape character + 字串逸出字元 @@ -2649,12 +2649,12 @@ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 型別 'System.Threading.Lock' 轉換為不同型別的值,在 'lock' 陳述式中可能會使用非預期的監視器型鎖定。 A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. + 型別 'System.Threading.Lock' 轉換為不同型別的值,在 'lock' 陳述式中可能會使用非預期的監視器型鎖定。 @@ -2679,32 +2679,32 @@ One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 一或多個具有非陣列參數集合參數的建構函式多載可能只適用於展開形式,但其在動態分派期間不支援。 One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more constructor overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 一或多個具有非陣列參數集合參數的建構函式多載可能只適用於展開形式,但其在動態分派期間不支援。 One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 一或多個具有非陣列參數集合參數的索引子多載可能只適用於展開形式,但其在動態分派期間不支援。 One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more indexer overloads having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 一或多個具有非陣列參數集合參數的索引子多載可能只適用於展開形式,但其在動態分派期間不支援。 One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method '{0}' having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 一或多個具有非陣列參數集合參數的方法 '{0}' 多載可能只適用於展開形式,但其在動態分派期間不支援。 One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. - One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. + 一或多個具有非陣列參數集合參數的方法多載可能只適用於展開形式,但其在動態分派期間不支援。 @@ -5084,13 +5084,13 @@ strument:TestCoverage 產生檢測要收集 - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider declaring the {0} as nullable. - 退出建構函式時,不可為 Null 的 {0} '{1}' 必須包含非 Null 值。請考慮將 {0} 宣告為可為 Null。 + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the {0} as nullable. + 退出建構函式時,不可為 Null 的 {0} '{1}' 必須包含非 Null 值。請考慮將 {0} 宣告為可為 Null。 - Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - 退出建構函式時,不可為 Null 的欄位必須包含非 Null 值。請考慮宣告為可為 Null。 + Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + 退出建構函式時,不可為 Null 的欄位必須包含非 Null 值。請考慮宣告為可為 Null。 @@ -10963,7 +10963,7 @@ You should consider suppressing the warning only if you're sure that you don't w Cannot specify a default value for a parameter collection - 無法指定參數陣列的預設值 + 無法指定參數收集的預設值 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs index a49eaad3395f5..6c4a94e3e62c7 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs @@ -231,7 +231,7 @@ public static async MyTask M() // (20,16): warning CS8603: Possible null reference return. // return await G((string?)null); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReturn, "await G((string?)null)").WithLocation(20, 16), - // (44,16): warning CS8618: Non-nullable field '_result' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (44,16): warning CS8618: Non-nullable field '_result' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // internal T _result; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "_result").WithArguments("field", "_result").WithLocation(44, 16) ); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs index f88aaf7a63080..d427589e7c5dd 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTests.cs @@ -4312,20 +4312,6 @@ .maxstack 5 public void ParamCallUsesCachedArray() { var verifier = CompileAndVerify(@" -namespace System -{ - public class Object { } - public class ValueType { } - public struct Int32 { } - public class String { } - public class Attribute { } - public struct Void { } - public class ParamArrayAttribute { } - public abstract class Array { - public static T[] Empty() { return new T[0]; } - } -} - public class Program { public static void Callee1(params object[] values) { } @@ -4365,7 +4351,7 @@ public static void M() Callee3(default(T), default(T)); } } -", verify: Verification.FailsPEVerify, options: TestOptions.ReleaseExe); +", options: TestOptions.ReleaseExe); verifier.VerifyIL("Program.M()", @"{ // Code size 297 (0x129) @@ -4470,19 +4456,7 @@ .maxstack 4 } "); - verifier = CompileAndVerify(@" -namespace System -{ - public class Object { } - public class ValueType { } - public struct Int32 { } - public class String { } - public class Attribute { } - public struct Void { } - public class ParamArrayAttribute { } - public abstract class Array { } -} - + var comp = CreateCompilation(@" public class Program { public static void Callee1(params object[] values) { } @@ -4498,7 +4472,12 @@ public static void M() Callee3(); } } -", verify: Verification.FailsPEVerify, options: TestOptions.ReleaseExe); +", options: TestOptions.ReleaseExe); + + comp.MakeMemberMissing(SpecialMember.System_Array__Empty); + + verifier = CompileAndVerify(comp); + verifier.VerifyIL("Program.M()", @"{ // Code size 34 (0x22) diff --git a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs index cbf51c6c062c2..b25606241d2b2 100644 --- a/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests.cs @@ -651,7 +651,8 @@ class Program Assert.True(attributeData.HasErrors); Assert.Equal("AAttribute..ctor(params System.Int32[] args)", attributeData.AttributeConstructor.ToTestDisplayString()); Assert.Equal(1, attributeData.AttributeConstructor.ParameterCount); - Assert.Equal(new object[] { 1, 2, 3 }, attributeData.ConstructorArguments.Select(arg => arg.Value)); + Assert.Equal(TypedConstantKind.Array, attributeData.ConstructorArguments.Single().Kind); + Assert.Equal(new object[] { 1, 2, 3 }, attributeData.ConstructorArguments.Single().Values.Select(arg => arg.Value)); // `SourceAttributeData.GetAttributeArgumentSyntax` asserts in debug mode when the attributeData has errors, so we don't test it here. } @@ -1025,12 +1026,14 @@ class Program { } Assert.Equal(3, arguments0.Length); Assert.Equal(true, arguments0[0].Value); Assert.Equal(1, arguments0[1].Value); + Assert.Equal(TypedConstantKind.Array, arguments0[2].Kind); Assert.Empty(arguments0[2].Values); var arguments1 = attrs[1].ConstructorArguments.ToArray(); Assert.Equal(3, arguments1.Length); Assert.Equal(true, arguments1[0].Value); Assert.Equal(1, arguments1[1].Value); + Assert.Equal(TypedConstantKind.Array, arguments1[2].Kind); Assert.Equal("a", arguments1[2].Values.Single().Value); } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 0835e1324bd32..6b8aab64b5594 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -3779,12 +3779,6 @@ static void Main() comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), - // 1.cs(6,23): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'List.Add(int)'. - // List l = [1]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("object", "System.Collections.Generic.List.Add(int)").WithLocation(6, 23), - // 1.cs(6,23): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // List l = [1]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "object", "int").WithLocation(6, 23), // 1.cs(7,16): error CS9174: Cannot initialize type 'IA' with a collection expression because the type is not constructible. // IA a = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.Collections.Generic.IA").WithLocation(7, 16), @@ -3849,12 +3843,6 @@ static void Main() comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), - // 1.cs(7,23): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'List.Add(int)'. - // List l = [1]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("object", "System.Collections.Generic.List.Add(int)").WithLocation(7, 23), - // 1.cs(7,23): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // List l = [1]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "object", "int").WithLocation(7, 23), // 1.cs(8,29): error CS9174: Cannot initialize type 'IEquatable' with a collection expression because the type is not constructible. // IEquatable e = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.IEquatable").WithLocation(8, 29)); @@ -4573,48 +4561,48 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(5, 21), - // (6,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[default]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(6, 21), - // (6,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[default]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(6, 21), - // (6,22): error CS8716: There is no target type for the default literal. - // _ = (string)[default]; - Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 22), - // (7,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[null]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[null]").WithArguments("string", "0").WithLocation(7, 21), - // (7,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[null]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("string", "Add").WithLocation(7, 21), - // (7,22): error CS0037: Cannot convert null to 'char' because it is a non-nullable value type - // _ = (string)[null]; - Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("char").WithLocation(7, 22), - // (8,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)['a']; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(8, 21), - // (8,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)['a']; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(8, 21), - // (9,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[1]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1]").WithArguments("string", "0").WithLocation(9, 21), - // (9,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[1]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1]").WithArguments("string", "Add").WithLocation(9, 21), - // (9,22): error CS0029: Cannot implicitly convert type 'int' to 'char' - // _ = (string)[1]; - Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "char").WithLocation(9, 22), - // (10,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments - // _ = (string)[..""]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[..""""]").WithArguments("string", "0").WithLocation(10, 21), - // (10,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // _ = (string)[..""]; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[..""""]").WithArguments("string", "Add").WithLocation(10, 21)); + // (5,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(5, 21), + // (6,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[default]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(6, 21), + // (6,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[default]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(6, 21), + // (6,22): error CS8716: There is no target type for the default literal. + // _ = (string)[default]; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 22), + // (7,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[null]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[null]").WithArguments("string", "0").WithLocation(7, 21), + // (7,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[null]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("string", "Add").WithLocation(7, 21), + // (7,22): error CS0037: Cannot convert null to 'char' because it is a non-nullable value type + // _ = (string)[null]; + Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("char").WithLocation(7, 22), + // (8,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)['a']; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(8, 21), + // (8,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)['a']; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(8, 21), + // (9,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[1]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1]").WithArguments("string", "0").WithLocation(9, 21), + // (9,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[1]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1]").WithArguments("string", "Add").WithLocation(9, 21), + // (9,22): error CS0029: Cannot implicitly convert type 'int' to 'char' + // _ = (string)[1]; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "char").WithLocation(9, 22), + // (10,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // _ = (string)[..""]; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[..""""]").WithArguments("string", "0").WithLocation(10, 21), + // (10,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = (string)[..""]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[..""""]").WithArguments("string", "Add").WithLocation(10, 21)); } [Fact] @@ -5330,13 +5318,7 @@ public void Add(int i) { } Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("C", "0").WithLocation(3, 5), // (4,5): error CS1729: 'C' does not contain a constructor that takes 0 arguments // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1, 2]").WithArguments("C", "0").WithLocation(4, 5), - // (4,5): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int)").WithLocation(4, 5), - // (4,5): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(4, 5)); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1, 2]").WithArguments("C", "0").WithLocation(4, 5)); } [WorkItem("https://github.com/dotnet/roslyn/pull/71492")] @@ -5408,17 +5390,13 @@ static void Main() object o; c = [1, 2]; o = (C)[3, 4]; + c.Report(); + o.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); - comp.VerifyEmitDiagnostics( - // 1.cs(7,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int)").WithLocation(7, 13), - // 1.cs(7,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(7, 13)); + CompileAndVerify(new[] { sourceA, sourceB2, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); } [Fact] @@ -5525,36 +5503,30 @@ public void CollectionInitializerType_08A() string source = """ using System; using System.Collections; + using System.Collections.Generic; struct S0 : IEnumerable { - public void Add(T t) { } - IEnumerator IEnumerable.GetEnumerator() => throw null; + private List _list; + public void Add(T t) { GetList().Add(t); } + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + private List GetList() => _list ??= new(); } class Program { - static void M0() + static void Main() { object o = (S0)[]; + o.Report(); o = (S0)[1, 2]; + o.Report(); S0 s = []; + s.Report(); s = [1, 2]; + s.Report(); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (13,22): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S0.Add(int)'. - // o = (S0)[1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "S0.Add(int)").WithLocation(13, 22), - // (13,22): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // o = (S0)[1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(13, 22), - // (15,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S0.Add(int)'. - // s = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "S0.Add(int)").WithLocation(15, 13), - // (15,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // s = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(15, 13)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [], [1, 2], "); } [Fact] @@ -5778,12 +5750,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (15,15): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(IA)'. + // (15,36): error CS0121: The call is ambiguous between the following methods or properties: 'C.Add(IA)' and 'C.Add(IB)' // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[(IA)null, (IB)null, new AB()]").WithArguments("object", "C.Add(IA)").WithLocation(15, 15), - // (15,15): error CS1503: Argument 1: cannot convert from 'object' to 'IA' - // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[(IA)null, (IB)null, new AB()]").WithArguments("1", "object", "IA").WithLocation(15, 15)); + Diagnostic(ErrorCode.ERR_AmbigCall, "new AB()").WithArguments("C.Add(IA)", "C.Add(IB)").WithLocation(15, 36)); } [Fact] @@ -5813,12 +5782,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (18,15): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'Extensions.Add(C, IA)'. - // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[(IA)null, (IB)null, new AB()]").WithArguments("object", "Extensions.Add(C, IA)").WithLocation(18, 15), - // (18,15): error CS1503: Argument 2: cannot convert from 'object' to 'IA' + // (18,36): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions.Add(C, IA)' and 'Extensions.Add(C, IB)' // C c = [(IA)null, (IB)null, new AB()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[(IA)null, (IB)null, new AB()]").WithArguments("2", "object", "IA").WithLocation(18, 15)); + Diagnostic(ErrorCode.ERR_AmbigCall, "new AB()").WithArguments("Extensions.Add(C, IA)", "Extensions.Add(C, IB)").WithLocation(18, 36)); } [Fact] @@ -5843,9 +5809,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S.Add(int, int)'. + // (13,13): error CS9215: Collection expression type 'S' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("object", "S.Add(int, int)").WithLocation(13, 13)); + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S").WithLocation(13, 13)); } [Fact] @@ -5872,9 +5838,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (15,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'int'. The best overloaded method is 'S.Add(int, int)'. + // (15,13): error CS9215: Collection expression type 'S' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("int", "S.Add(int, int)").WithLocation(15, 13)); + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S").WithLocation(15, 13)); } [Fact] @@ -5904,9 +5870,9 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (18,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'int'. The best overloaded method is 'Extensions.Add(S, T, T)'. + // (18,13): error CS9215: Collection expression type 'S' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("int", "Extensions.Add(S, T, T)").WithLocation(18, 13)); + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S").WithLocation(18, 13)); } [Fact] @@ -5926,18 +5892,13 @@ class Program static void Main() { C c = []; + c.Report(); c = [1, 2]; + c.Report(); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (14,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int, int)'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int, int)").WithLocation(14, 13), - // (14,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(14, 13)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] @@ -5984,18 +5945,13 @@ class Program static void Main() { C c = []; + c.Report(); c = [1, 2]; + c.Report(); } } """; - var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (14,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int, params int[])'. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2]").WithArguments("object", "C.Add(int, params int[])").WithLocation(14, 13), - // (14,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2]").WithArguments("1", "object", "int").WithLocation(14, 13)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] @@ -6112,18 +6068,12 @@ class Program """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,40): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S.Add(T)'. - // static S Create(T t, U u) => [t, u]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[t, u]").WithArguments("object", "S.Add(T)").WithLocation(7, 40), - // (7,40): error CS1503: Argument 1: cannot convert from 'object' to 'T' - // static S Create(T t, U u) => [t, u]; - Diagnostic(ErrorCode.ERR_BadArgType, "[t, u]").WithArguments("1", "object", "T").WithLocation(7, 40), - // (11,46): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'S.Add(T)'. + // (11,50): error CS1950: The best overloaded Add method 'S.Add(T)' for the collection initializer has some invalid arguments // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, y]").WithArguments("object", "S.Add(T)").WithLocation(11, 46), - // (11,46): error CS1503: Argument 1: cannot convert from 'object' to 'T' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("S.Add(T)").WithLocation(11, 50), + // (11,50): error CS1503: Argument 1: cannot convert from 'U' to 'T' // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_BadArgType, "[x, y]").WithArguments("1", "object", "T").WithLocation(11, 46)); + Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "U", "T").WithLocation(11, 50)); } [Fact] @@ -6179,12 +6129,6 @@ class Program // (9,41): error CS0029: Cannot implicitly convert type 'T' to 'U' // static S Create(T t, U u) => [t, u]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "t").WithArguments("T", "U").WithLocation(9, 41), - // (13,46): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'U'. The best overloaded method is 'S.Add(T)'. - // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, y]").WithArguments("U", "S.Add(T)").WithLocation(13, 46), - // (13,46): error CS1503: Argument 1: cannot convert from 'U' to 'T' - // static S Create(T x, U y) => [x, y]; - Diagnostic(ErrorCode.ERR_BadArgType, "[x, y]").WithArguments("1", "U", "T").WithLocation(13, 46), // (13,47): error CS0029: Cannot implicitly convert type 'T' to 'U' // static S Create(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(13, 47)); @@ -6695,9 +6639,9 @@ static void Main() // (16,42): error CS0411: The type arguments for method 'Extensions.Add(ref MyCollection, out T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // MyCollection x = new() { 1 }; Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "1").WithArguments("Extensions.Add(ref MyCollection, out T)").WithLocation(16, 42), - // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, out object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, out T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection z = [..x, ..y, 3]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, out object)").WithLocation(18, 34)); + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, out T)").WithLocation(18, 34)); } [Fact] @@ -6730,9 +6674,9 @@ static void Main() // (16,42): error CS0411: The type arguments for method 'Extensions.Add(ref MyCollection, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // MyCollection x = new() { 1 }; Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "1").WithArguments("Extensions.Add(ref MyCollection, ref T)").WithLocation(16, 42), - // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (18,34): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection z = [..x, ..y, 3]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, ref object)").WithLocation(18, 34)); + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add(ref MyCollection, ref T)").WithLocation(18, 34)); } [Fact] @@ -6837,12 +6781,12 @@ static void Main() // (20,21): error CS1503: Argument 2: cannot convert from 'int' to 'string' // y = new() { 3 }; Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(20, 21), - // (21,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'int'. The best overloaded method is 'Extensions.Add(ref MyCollection, string)'. + // (21,14): error CS1950: The best overloaded Add method 'Extensions.Add(ref MyCollection, string)' for the collection initializer has some invalid arguments // y = [4]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[4]").WithArguments("int", "Extensions.Add(ref MyCollection, string)").WithLocation(21, 13), - // (21,13): error CS1503: Argument 2: cannot convert from 'int' to 'string' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "4").WithArguments("Extensions.Add(ref MyCollection, string)").WithLocation(21, 14), + // (21,14): error CS1503: Argument 2: cannot convert from 'int' to 'string' // y = [4]; - Diagnostic(ErrorCode.ERR_BadArgType, "[4]").WithArguments("2", "int", "string").WithLocation(21, 13)); + Diagnostic(ErrorCode.ERR_BadArgType, "4").WithArguments("2", "int", "string").WithLocation(21, 14)); } [Fact] @@ -6878,15 +6822,15 @@ static void Main() // (17,21): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // x = new() { "1" }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"""1""").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(17, 21), - // (18,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (18,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // x = ["2"]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"[""2""]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(18, 13), + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"[""2""]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(18, 13), // (20,21): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // y = new() { 3 }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "3").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(20, 21), - // (21,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // (21,13): error CS1954: The best overloaded method match 'Extensions.Add(ref MyCollection, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // y = [4]; - Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[4]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(21, 13)); + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[4]").WithArguments("Extensions.Add(ref MyCollection, ref string)").WithLocation(21, 13)); } [Fact] @@ -8040,13 +7984,13 @@ static void Main() comp = CreateCompilation(new[] { sourceC, s_collectionExtensions }, references: new[] { refB }); comp.VerifyEmitDiagnostics( - // 0.cs(6,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // 0.cs(6,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = []; Diagnostic(ErrorCode.ERR_NoTypeDef, "[]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 13), - // 0.cs(8,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // 0.cs(8,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = [1, 2]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[1, 2]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 13), - // 0.cs(13,13): error CS0012: The type 'A2' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // 0.cs(13,14): error CS0012: The type 'A2' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // y = [3, 4]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[3, 4]").WithArguments("A2", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(13, 13)); } @@ -8147,12 +8091,12 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'KeyValuePair'. The best overloaded method is 'Dictionary.Add(int, int)'. + // (7,13): error CS9215: Collection expression type 'Dictionary' must have an instance or extension method 'Add' that can be called with a single argument. // d = [default]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[default]").WithArguments("System.Collections.Generic.KeyValuePair", "System.Collections.Generic.Dictionary.Add(int, int)").WithLocation(7, 13), - // (8,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'KeyValuePair'. The best overloaded method is 'Dictionary.Add(int, int)'. + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[default]").WithArguments("System.Collections.Generic.Dictionary").WithLocation(7, 13), + // (8,13): error CS9215: Collection expression type 'Dictionary' must have an instance or extension method 'Add' that can be called with a single argument. // d = [new KeyValuePair(1, 2)]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new KeyValuePair(1, 2)]").WithArguments("System.Collections.Generic.KeyValuePair", "System.Collections.Generic.Dictionary.Add(int, int)").WithLocation(8, 13), + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new KeyValuePair(1, 2)]").WithArguments("System.Collections.Generic.Dictionary").WithLocation(8, 13), // (9,15): error CS1003: Syntax error, ',' expected // d = [3:4]; Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",").WithLocation(9, 15), @@ -9433,18 +9377,42 @@ static void Main() if (targetElementType == "int") { comp.VerifyEmitDiagnostics( - // 1.cs(10,26): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(int)'. + // 1.cs(10,27): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection c = [..d1, ..d2, ..e1, ..e2]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..d1, ..d2, ..e1, ..e2]").WithArguments("object", "MyCollection.Add(int)").WithLocation(10, 26), - // 1.cs(10,26): error CS1503: Argument 1: cannot convert from 'object' to 'int' + Diagnostic(ErrorCode.ERR_BadArgType, "..d1").WithArguments("1", "object", "int").WithLocation(10, 27), + // 1.cs(10,29): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // MyCollection c = [..d1, ..d2, ..e1, ..e2]; - Diagnostic(ErrorCode.ERR_BadArgType, "[..d1, ..d2, ..e1, ..e2]").WithArguments("1", "object", "int").WithLocation(10, 26), - // 1.cs(14,13): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection.Add(int)'. + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "d1").WithArguments("MyCollection.Add(int)").WithLocation(10, 29), + // 1.cs(10,33): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgType, "..d2").WithArguments("1", "object", "int").WithLocation(10, 33), + // 1.cs(10,35): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "d2").WithArguments("MyCollection.Add(int)").WithLocation(10, 35), + // 1.cs(10,39): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgType, "..e1").WithArguments("1", "object", "int").WithLocation(10, 39), + // 1.cs(10,41): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "e1").WithArguments("MyCollection.Add(int)").WithLocation(10, 41), + // 1.cs(10,45): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgType, "..e2").WithArguments("1", "object", "int").WithLocation(10, 45), + // 1.cs(10,47): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // MyCollection c = [..d1, ..d2, ..e1, ..e2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "e2").WithArguments("MyCollection.Add(int)").WithLocation(10, 47), + // 1.cs(14,14): error CS1503: Argument 1: cannot convert from 'object' to 'int' + // c = [..(dynamic)x, ..(IEnumerable)y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..(dynamic)x").WithArguments("1", "object", "int").WithLocation(14, 14), + // 1.cs(14,16): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments + // c = [..(dynamic)x, ..(IEnumerable)y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "(dynamic)x").WithArguments("MyCollection.Add(int)").WithLocation(14, 16), + // 1.cs(14,28): error CS1503: Argument 1: cannot convert from 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..(dynamic)x, ..(IEnumerable)y]").WithArguments("object", "MyCollection.Add(int)").WithLocation(14, 13), - // 1.cs(14,13): error CS1503: Argument 1: cannot convert from 'object' to 'int' + Diagnostic(ErrorCode.ERR_BadArgType, "..(IEnumerable)y").WithArguments("1", "object", "int").WithLocation(14, 28), + // 1.cs(14,30): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // c = [..(dynamic)x, ..(IEnumerable)y]; - Diagnostic(ErrorCode.ERR_BadArgType, "[..(dynamic)x, ..(IEnumerable)y]").WithArguments("1", "object", "int").WithLocation(14, 13)); + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "(IEnumerable)y").WithArguments("MyCollection.Add(int)").WithLocation(14, 30)); } else { @@ -10892,7 +10860,7 @@ .locals init (System.Collections.Generic.IEnumerable V_0) //y """); comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); - comp.MakeMemberMissing(WellKnownMember.System_Array__Empty); + comp.MakeMemberMissing(SpecialMember.System_Array__Empty); verifier = CompileAndVerify(comp, expectedOutput: "[], [], "); verifier.VerifyIL("Program.Main", """ @@ -12186,6 +12154,19 @@ void verifyInterfaces(ModuleSymbol module, string typeName) [Theory] [InlineData((int)SpecialMember.System_Collections_IEnumerable__GetEnumerator, "System.Collections.IEnumerable", "GetEnumerator")] [InlineData((int)SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, "System.Collections.Generic.IEnumerable`1", "GetEnumerator")] + [InlineData((int)SpecialMember.System_Collections_Generic_IReadOnlyCollection_T__Count, "System.Collections.Generic.IReadOnlyCollection`1", "Count")] + [InlineData((int)SpecialMember.System_Collections_Generic_IReadOnlyList_T__get_Item, "System.Collections.Generic.IReadOnlyList`1", "get_Item")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Count, "System.Collections.Generic.ICollection`1", "Count")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__IsReadOnly, "System.Collections.Generic.ICollection`1", "IsReadOnly")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Add, "System.Collections.Generic.ICollection`1", "Add")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Clear, "System.Collections.Generic.ICollection`1", "Clear")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Contains, "System.Collections.Generic.ICollection`1", "Contains")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__CopyTo, "System.Collections.Generic.ICollection`1", "CopyTo")] + [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Remove, "System.Collections.Generic.ICollection`1", "Remove")] + [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__get_Item, "System.Collections.Generic.IList`1", "get_Item")] + [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__IndexOf, "System.Collections.Generic.IList`1", "IndexOf")] + [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__Insert, "System.Collections.Generic.IList`1", "Insert")] + [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__RemoveAt, "System.Collections.Generic.IList`1", "RemoveAt")] public void SynthesizedReadOnlyList_MissingSpecialMembers(int missingMember, string missingMemberTypeName, string missingMemberName) { string source = """ @@ -12295,19 +12276,6 @@ static void Main() [InlineData((int)WellKnownMember.System_Collections_IList__Insert, "System.Collections.IList", "Insert")] [InlineData((int)WellKnownMember.System_Collections_IList__Remove, "System.Collections.IList", "Remove")] [InlineData((int)WellKnownMember.System_Collections_IList__RemoveAt, "System.Collections.IList", "RemoveAt")] - [InlineData((int)WellKnownMember.System_Collections_Generic_IReadOnlyCollection_T__Count, "System.Collections.Generic.IReadOnlyCollection`1", "Count")] - [InlineData((int)WellKnownMember.System_Collections_Generic_IReadOnlyList_T__get_Item, "System.Collections.Generic.IReadOnlyList`1", "get_Item")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__Count, "System.Collections.Generic.ICollection`1", "Count")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__IsReadOnly, "System.Collections.Generic.ICollection`1", "IsReadOnly")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__Add, "System.Collections.Generic.ICollection`1", "Add")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__Clear, "System.Collections.Generic.ICollection`1", "Clear")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__Contains, "System.Collections.Generic.ICollection`1", "Contains")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__CopyTo, "System.Collections.Generic.ICollection`1", "CopyTo")] - [InlineData((int)WellKnownMember.System_Collections_Generic_ICollection_T__Remove, "System.Collections.Generic.ICollection`1", "Remove")] - [InlineData((int)WellKnownMember.System_Collections_Generic_IList_T__get_Item, "System.Collections.Generic.IList`1", "get_Item")] - [InlineData((int)WellKnownMember.System_Collections_Generic_IList_T__IndexOf, "System.Collections.Generic.IList`1", "IndexOf")] - [InlineData((int)WellKnownMember.System_Collections_Generic_IList_T__Insert, "System.Collections.Generic.IList`1", "Insert")] - [InlineData((int)WellKnownMember.System_Collections_Generic_IList_T__RemoveAt, "System.Collections.Generic.IList`1", "RemoveAt")] [InlineData((int)WellKnownMember.System_NotSupportedException__ctor, "System.NotSupportedException", ".ctor")] public void SynthesizedReadOnlyList_MissingWellKnownMembers(int missingMember, string missingMemberTypeName, string missingMemberName) { @@ -13431,6 +13399,56 @@ private static void VerifyTypes(SemanticModel model, ExpressionSyntax expr, stri Assert.Equal(expectedConversionKind, conversion.Kind); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72541")] + public void NamedArgumentConversion() + { + var source = """ + #nullable enable + using System.Collections.Generic; + + static class C + { + static void Main() + { + C.M(y: [new D { }]); + } + static void M(string x, IReadOnlyList y) { } + } + + class D { } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,11): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'C.M(string, IReadOnlyList)' + // C.M(y: [new D { }]); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("x", "C.M(string, System.Collections.Generic.IReadOnlyList)").WithLocation(8, 11)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72541")] + public void NamedArgumentConversion_CollectionInitializer() + { + var source = """ + #nullable enable + using System.Collections.Generic; + + static class C + { + static void Main() + { + C.M(y: new() { new D() { } }); + } + static void M(string x, List y) { } + } + + class D { } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,11): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'C.M(string, List)' + // C.M(y: new() { new D() { } }); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("x", "C.M(string, System.Collections.Generic.List)").WithLocation(8, 11)); + } + [CombinatorialData] [Theory] public void CollectionBuilder_01(bool useCompilationReference) @@ -25133,11 +25151,11 @@ .. GetConfig(), var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (4,52): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'KeyValuePair'. The best overloaded method is 'Dictionary.Add(string, object)'. + // (4,52): error CS9215: Collection expression type 'Dictionary' must have an instance or extension method 'Add' that can be called with a single argument. // Dictionary Config => /**/[ Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, @"[ .. GetConfig(), - ]").WithArguments("System.Collections.Generic.KeyValuePair", "System.Collections.Generic.Dictionary.Add(string, object)").WithLocation(4, 52)); + ]").WithArguments("System.Collections.Generic.Dictionary").WithLocation(4, 52)); VerifyOperationTreeForTest(comp, """ @@ -25155,410 +25173,980 @@ .. GetConfig(), } [Fact] - public void Async_01() + public void IOperation_AmbiguousAdd_01() { - string source = """ - using System.Collections.Generic; - using System.Threading.Tasks; + string sourceA = """ + using System.Collections; + interface IA { } + interface IB { } + class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(IA a) => throw null; + public void Add(IB b) => throw null; + public void Add(object o) => throw null; + } + """; + string sourceB = """ + class C : IA, IB { } class Program { - static async Task Main() + static MyCollection Create(C x, C[] y) { - (await CreateArray()).Report(); - (await CreateList()).Report(); - } - static async Task CreateArray() - { - return [await F(1), await F(2)]; - } - static async Task> CreateList() - { - return [await F(3), await F(4)]; - } - static async Task F(int i) - { - await Task.Yield(); - return i; + return /**/[x, ..y]/**/; } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 27), + // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void Async_02() + public void IOperation_AmbiguousAdd_02() { - string source = """ - using System.Collections.Generic; - using System.Threading.Tasks; + string sourceA = """ + using System.Collections; + interface IA { } + interface IB { } + class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(IA a) => throw null; + public void Add(IB b) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, object o) { } + } + """; + string sourceB = """ + class C : IA, IB { } class Program { - static async Task Main() - { - (await F2(F1())).Report(); - } - static async Task F1() - { - return [await F(1), await F(2)]; - } - static async Task F2(Task e) - { - return [3, .. await e, 4]; - } - static async Task F(T t) + static MyCollection Create(C x, C[] y) { - await Task.Yield(); - return t; + return /**/[x, ..y]/**/; } } """; - CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[3, 1, 2, 4], "); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 27), + // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void PostfixIncrementDecrement() + public void IOperation_AmbiguousAdd_03() { - string source = """ + string sourceA = """ + using System.Collections; using System.Collections.Generic; - - class Program + class MyCollection : IEnumerable { - static void Main() + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + static class ExtensionsA + { + public static void Add(this MyCollection collection, string s) { } + } + static class ExtensionsB + { + public static void Add(this MyCollection collection, string s) { } + } + namespace N + { + static class ExtensionsC { - []++; - []--; + public static void Add(this MyCollection collection, T t) { } } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer - // []++; - Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(7, 9), - // (8,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer - // []--; - Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(8, 9)); - } - - [Fact] - public void PostfixPointerAccess() - { - string source = """ - using System.Collections.Generic; - + string sourceB = """ + using N; class Program { - static void Main() + static MyCollection Create(string x, string[] y) { - var v = []->Count; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,17): error CS9503: There is no target type for the collection expression. - // var v = []->Count; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 17)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add(MyCollection, string)' and 'ExtensionsB.Add(MyCollection, string)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("ExtensionsA.Add(MyCollection, string)", "ExtensionsB.Add(MyCollection, string)").WithLocation(6, 27), + // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add(MyCollection, string)' and 'ExtensionsB.Add(MyCollection, string)' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("ExtensionsA.Add(MyCollection, string)", "ExtensionsB.Add(MyCollection, string)").WithLocation(6, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.String) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.String[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.String) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.String[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void LeftHandAssignment() + public void IOperation_InvalidAdd_01() { - string source = """ - using System.Collections.Generic; - - class Program + string sourceA = """ + using System.Collections; + public class MyCollection : IEnumerable { - static void Main() - { - [] = null; - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(string s) { } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // [] = null; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[]").WithLocation(7, 9)); - } - - [Fact] - public void BinaryOperator() - { - string source = """ - using System.Collections.Generic; - + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create() { - [] + list; + return /**/[F1(), ..F2()]/**/; } + static int F1() => 1; + static int[] F2() => [2, 3]; } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS0019: Operator '+' cannot be applied to operands of type 'collection expressions' and 'List' - // [] + list; - Diagnostic(ErrorCode.ERR_BadBinaryOps, "[] + list").WithArguments("+", "collection expressions", "System.Collections.Generic.List").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] + list; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] + list").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,27): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F1()").WithArguments("MyCollection.Add(string)").WithLocation(5, 27), + // (5,27): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "F1()").WithArguments("1", "int", "string").WithLocation(5, 27), + // (5,33): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "..F2()").WithArguments("1", "int", "string").WithLocation(5, 33), + // (5,35): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F2()").WithArguments("MyCollection.Add(string)").WithLocation(5, 35)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[F1(), ..F2()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void RangeOperator() + public void IOperation_InvalidAdd_02() { - string source = """ - using System.Collections.Generic; - - class Program + string sourceA = """ + using System.Collections; + public class MyCollection : IEnumerable { - static void Main(List list) - { - []..; - } + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + public static class Extensions + { + public static void Add(this MyCollection collection, string s) { } } """; - CreateCompilationWithIndexAndRangeAndSpan(source).VerifyEmitDiagnostics( - // (7,9): error CS9500: Cannot initialize type 'Index' with a collection expression because the type is not constructible. - // []..; - Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("System.Index").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // []..; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[]..").WithLocation(7, 9)); - } - - [Fact] - public void TopLevelSwitchExpression() - { - string source = """ - using System.Collections.Generic; - + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create() { - [] switch { null => 0 }; + return /**/[F1(), ..F2()]/**/; } + static int F1() => 1; + static int[] F2() => [2, 3]; } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] switch - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] switch - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] switch { null => 0 }").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,27): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F1()").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(5, 27), + // (5,27): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "F1()").WithArguments("2", "int", "string").WithLocation(5, 27), + // (5,33): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "..F2()").WithArguments("2", "int", "string").WithLocation(5, 33), + // (5,35): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // return /**/[F1(), ..F2()]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F2()").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(5, 35)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[F1(), ..F2()]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[F1(), ..F2()]') + Elements(2): + IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') + Instance Receiver: + null + Arguments(0) + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') + Operand: + IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') + Instance Receiver: + null + Arguments(0) + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void TopLevelWithExpression() + public void IOperation_InvalidAdd_03() { - string source = """ - using System.Collections.Generic; - + string sourceA = """ + using System.Collections; + public class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + public static class Extensions + { + public static void Add(this MyCollection collection, params string[] args) { } + } + """; + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create(int x, int[] y) { - [] with { Count = 1, }; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] with { Count = 1, }; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] with { Count = 1, }; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] with { Count = 1, }").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,27): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, params string[])' for the collection initializer has some invalid arguments + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add(MyCollection, params string[])").WithLocation(5, 27), + // (5,27): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "string").WithLocation(5, 27), + // (5,30): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "string").WithLocation(5, 30), + // (5,32): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, params string[])' for the collection initializer has some invalid arguments + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions.Add(MyCollection, params string[])").WithLocation(5, 32)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionExpression) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void TopLevelIsExpressions() + public void IOperation_InvalidAdd_04() { - string source = """ - using System.Collections.Generic; - + string sourceA = """ + using System; + using System.Collections; + public class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + public Action Add; + } + """; + string sourceB = """ class Program { - static void Main(List list) + static MyCollection Create(int x, int[] y) { - [] is object; + return /**/[x, ..y]/**/; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] is object; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] is object; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] is object").WithLocation(7, 9)); + var comp = CreateCompilation([sourceB, sourceA]); + comp.VerifyEmitDiagnostics( + // (5,26): error CS0118: 'Add' is a field but is used like a method + // return /**/[x, ..y]/**/; + Diagnostic(ErrorCode.ERR_BadSKknown, "[x, ..y]").WithArguments("Add", "field", "method").WithLocation(5, 26)); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + """); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, + """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') + Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Operand: + ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') + Elements(2): + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') + Operand: + IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (NoConversion) + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """); } [Fact] - public void TopLevelAsExpressions() + public void Async_01() { string source = """ using System.Collections.Generic; - + using System.Threading.Tasks; class Program { - static void Main(List list) + static async Task Main() { - [] as List; + (await CreateArray()).Report(); + (await CreateList()).Report(); + } + static async Task CreateArray() + { + return [await F(1), await F(2)]; + } + static async Task> CreateList() + { + return [await F(3), await F(4)]; + } + static async Task F(int i) + { + await Task.Yield(); + return i; } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (7,9): error CS9503: There is no target type for the collection expression. - // [] as List; - Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), - // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement - // [] as List; - Diagnostic(ErrorCode.ERR_IllegalStatement, "[] as List").WithLocation(7, 9)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute() + [Fact] + public void Async_02() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); - - [X([42, 43, 44])] - class C - { - } - - public class XAttribute : System.Attribute + using System.Collections.Generic; + using System.Threading.Tasks; + class Program { - public int[] _values; - public XAttribute(int[] values) { _values = values; } + static async Task Main() + { + (await F2(F1())).Report(); + } + static async Task F1() + { + return [await F(1), await F(2)]; + } + static async Task F2(Task e) + { + return [3, .. await e, 4]; + } + static async Task F(T t) + { + await Task.Yield(); + return t; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); - - var program = comp.GetMember("C"); - var argument = program.GetAttributes().Single().ConstructorArguments.Single(); - var values = argument.Values; - Assert.Equal(3, values.Length); - Assert.Equal(42, values[0].Value); - Assert.Equal(43, values[1].Value); - Assert.Equal(44, values[2].Value); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[3, 1, 2, 4], "); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_Named() + [Fact] + public void PostfixIncrementDecrement() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr.Values.Report(); - - [X(Values = [42, 43, 44])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public int[] Values { get; set; } + static void Main() + { + []++; + []--; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // []++; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(7, 9), + // (8,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // []--; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(8, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_Params() + [Fact] + public void PostfixPointerAccess() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); - - [X([42, 43, 44])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public int[] _values; - public XAttribute(params int[] values) { _values = values; } + static void Main() + { + var v = []->Count; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,17): error CS9503: There is no target type for the collection expression. + // var v = []->Count; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 17)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_StringConstants() + [Fact] + public void LeftHandAssignment() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - attr._values.Report(); - - [X(["hi", null])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public string[] _values; - public XAttribute(string[] values) { _values = values; } + static void Main() + { + [] = null; + } } """; - - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[hi, null],"); + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // [] = null; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[]").WithLocation(7, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NestedArray() + [Fact] + public void BinaryOperator() { string source = """ - [X([[1], [2]])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public XAttribute(int[][] values) { } + static void Main(List list) + { + [] + list; + } } """; - CreateCompilation(source).VerifyEmitDiagnostics( - // (1,2): error CS0181: Attribute constructor parameter 'values' has type 'int[][]', which is not a valid attribute parameter type - // [X([[1], [2]])] - Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("values", "int[][]").WithLocation(1, 2) - ); + // (7,9): error CS0019: Operator '+' cannot be applied to operands of type 'collection expressions' and 'List' + // [] + list; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "[] + list").WithArguments("+", "collection expressions", "System.Collections.Generic.List").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] + list; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] + list").WithLocation(7, 9)); } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] - public void InAttribute_NestedArrayAsObject() + [Fact] + public void RangeOperator() { string source = """ - var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); - var inner = (int[])attr._values[0]; - inner.Report(); - - [X([(int[])[1]])] - class C - { - } + using System.Collections.Generic; - public class XAttribute : System.Attribute + class Program { - public object[] _values; - public XAttribute(object[] values) { _values = values; } + static void Main(List list) + { + []..; + } } """; + CreateCompilationWithIndexAndRangeAndSpan(source).VerifyEmitDiagnostics( + // (7,9): error CS9500: Cannot initialize type 'Index' with a collection expression because the type is not constructible. + // []..; + Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("System.Index").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // []..; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[]..").WithLocation(7, 9)); + } - var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + [Fact] + public void TopLevelSwitchExpression() + { + string source = """ + using System.Collections.Generic; + + class Program + { + static void Main(List list) + { + [] switch { null => 0 }; + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS9503: There is no target type for the collection expression. + // [] switch + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] switch + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] switch { null => 0 }").WithLocation(7, 9)); + } + + [Fact] + public void TopLevelWithExpression() + { + string source = """ + using System.Collections.Generic; + + class Program + { + static void Main(List list) + { + [] with { Count = 1, }; + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS9503: There is no target type for the collection expression. + // [] with { Count = 1, }; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] with { Count = 1, }; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] with { Count = 1, }").WithLocation(7, 9)); + } + + [Fact] + public void TopLevelIsExpressions() + { + string source = """ + using System.Collections.Generic; + + class Program + { + static void Main(List list) + { + [] is object; + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS9503: There is no target type for the collection expression. + // [] is object; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] is object; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] is object").WithLocation(7, 9)); + } + + [Fact] + public void TopLevelAsExpressions() + { + string source = """ + using System.Collections.Generic; + + class Program + { + static void Main(List list) + { + [] as List; + } + } + """; + CreateCompilation(source).VerifyEmitDiagnostics( + // (7,9): error CS9503: There is no target type for the collection expression. + // [] as List; + Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), + // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // [] as List; + Diagnostic(ErrorCode.ERR_IllegalStatement, "[] as List").WithLocation(7, 9)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); + + [X([42, 43, 44])] + class C + { + } + + public class XAttribute : System.Attribute + { + public int[] _values; + public XAttribute(int[] values) { _values = values; } + } + """; + + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + + var program = comp.GetMember("C"); + var argument = program.GetAttributes().Single().ConstructorArguments.Single(); + var values = argument.Values; + Assert.Equal(3, values.Length); + Assert.Equal(42, values[0].Value); + Assert.Equal(43, values[1].Value); + Assert.Equal(44, values[2].Value); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_Named() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr.Values.Report(); + + [X(Values = [42, 43, 44])] + class C + { + } + + public class XAttribute : System.Attribute + { + public int[] Values { get; set; } + } + """; + + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_Params() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); + + [X([42, 43, 44])] + class C + { + } + + public class XAttribute : System.Attribute + { + public int[] _values; + public XAttribute(params int[] values) { _values = values; } + } + """; + + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_StringConstants() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + attr._values.Report(); + + [X(["hi", null])] + class C + { + } + + public class XAttribute : System.Attribute + { + public string[] _values; + public XAttribute(string[] values) { _values = values; } + } + """; + + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[hi, null],"); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NestedArray() + { + string source = """ + [X([[1], [2]])] + class C + { + } + + public class XAttribute : System.Attribute + { + public XAttribute(int[][] values) { } + } + """; + + CreateCompilation(source).VerifyEmitDiagnostics( + // (1,2): error CS0181: Attribute constructor parameter 'values' has type 'int[][]', which is not a valid attribute parameter type + // [X([[1], [2]])] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("values", "int[][]").WithLocation(1, 2) + ); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] + public void InAttribute_NestedArrayAsObject() + { + string source = """ + var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); + var inner = (int[])attr._values[0]; + inner.Report(); + + [X([(int[])[1]])] + class C + { + } + + public class XAttribute : System.Attribute + { + public object[] _values; + public XAttribute(object[] values) { _values = values; } + } + """; + + var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[1],"); } @@ -26008,13 +26596,7 @@ public static void M(int[] i) { } var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (3,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(3, 7), - // (3,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("object", "C.Add(int)").WithLocation(3, 7), - // (3,7): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "object", "int").WithLocation(3, 7) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(3, 7) ); var tree = comp.SyntaxTrees.First(); @@ -26057,13 +26639,7 @@ public static void M(int[] i) { } var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(4, 7), - // (4,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'C.Add(int)'. - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..values]").WithArguments("object", "C.Add(int)").WithLocation(4, 7), - // (4,7): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[..values]").WithArguments("1", "object", "int").WithLocation(4, 7) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(4, 7) ); var tree = comp.SyntaxTrees.First(); @@ -26104,12 +26680,6 @@ public void Add(int i) { } // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(4, 7), - // (4,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'string'. The best overloaded method is 'C.Add(int)'. - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1]").WithArguments("string", "C.Add(int)").WithLocation(4, 7), - // (4,7): error CS1503: Argument 1: cannot convert from 'string' to 'int' - // C x = [1]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "string", "int").WithLocation(4, 7), // (4,8): error CS0029: Cannot implicitly convert type 'int' to 'string' // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(4, 8) @@ -26149,12 +26719,6 @@ public static void M(int[] i) { } // (5,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(5, 7), - // (5,7): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'string'. The best overloaded method is 'C.Add(int)'. - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[..values]").WithArguments("string", "C.Add(int)").WithLocation(5, 7), - // (5,7): error CS1503: Argument 1: cannot convert from 'string' to 'int' - // C x = [..values]; // 1 - Diagnostic(ErrorCode.ERR_BadArgType, "[..values]").WithArguments("1", "string", "int").WithLocation(5, 7), // (5,10): error CS0029: Cannot implicitly convert type 'int' to 'string' // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "values").WithArguments("int", "string").WithLocation(5, 10) @@ -26847,20 +27411,11 @@ class MyCollection2 : IEnumerable where TAdd : TElemen } """; - var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); - comp.VerifyEmitDiagnostics( - // 0.cs(4,24): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection1.Add(int)'. - // MyCollection1 x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2, 3]").WithArguments("object", "MyCollection1.Add(int)").WithLocation(4, 24), - // 0.cs(4,24): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // MyCollection1 x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2, 3]").WithArguments("1", "object", "int").WithLocation(4, 24), - // 0.cs(6,32): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection2.Add(int)'. - // MyCollection2 y = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2, 3]").WithArguments("object", "MyCollection2.Add(int)").WithLocation(6, 32), - // 0.cs(6,32): error CS1503: Argument 1: cannot convert from 'object' to 'int' - // MyCollection2 y = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2, 3]").WithArguments("1", "object", "int").WithLocation(6, 32)); + CompileAndVerify( + new[] { source, s_collectionExtensions }, + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify, + expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); } [Fact] @@ -26883,19 +27438,19 @@ class MyCollection2 : IEnumerable where TAdd : TElemen var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // (4,32): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'MyCollection2.Add(int)'. + // (4,33): error CS1950: The best overloaded Add method 'MyCollection2.Add(int)' for the collection initializer has some invalid arguments // MyCollection2 y = [new object()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new object()]").WithArguments("object", "MyCollection2.Add(int)").WithLocation(4, 32), - // (4,32): error CS1503: Argument 1: cannot convert from 'object' to 'int' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "new object()").WithArguments("MyCollection2.Add(int)").WithLocation(4, 33), + // (4,33): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection2 y = [new object()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[new object()]").WithArguments("1", "object", "int").WithLocation(4, 32) + Diagnostic(ErrorCode.ERR_BadArgType, "new object()").WithArguments("1", "object", "int").WithLocation(4, 33) ); } [Fact] public void GenericIEnumerable_DifferentConversionToAdd() { - // For purpose of conversion, we rely on conversion from numeric literal to uint (from IEnumerable) + // For purpose of conversion, we rely on the existence of an Add method. // But for purpose of construction, we rely on conversion from numeric literal to sbyte (from Add(sbyte)) string source = """ using System.Collections; @@ -26913,21 +27468,18 @@ class MyCollection : IEnumerable } """; - var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); - comp.VerifyEmitDiagnostics( - // 0.cs(4,18): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'uint'. The best overloaded method is 'MyCollection.Add(sbyte)'. - // MyCollection x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, 2, 3]").WithArguments("uint", "MyCollection.Add(sbyte)").WithLocation(4, 18), - // 0.cs(4,18): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' - // MyCollection x = [1, 2, 3]; - Diagnostic(ErrorCode.ERR_BadArgType, "[1, 2, 3]").WithArguments("1", "uint", "sbyte").WithLocation(4, 18)); + CompileAndVerify( + new[] { source, s_collectionExtensions }, + targetFramework: TargetFramework.Net70, + verify: Verification.FailsPEVerify, + expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); } [Fact] public void GenericIEnumerable_NoConversionToAdd() { - // For purpose of conversion, we rely on conversion from numeric literal to uint (from IEnumerable) - // But for purpose of construction, we rely on conversion from numeric literal to sbyte (from Add(sbyte)) + // For purpose of conversion, we rely on the existence of an Add method. + // But for purpose of construction, there is no conversion from uint to sbyte (from Add(sbyte)) string source = """ using System.Collections; using System.Collections.Generic; @@ -26945,12 +27497,12 @@ class MyCollection : IEnumerable var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( - // 0.cs(4,18): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'uint'. The best overloaded method is 'MyCollection.Add(sbyte)'. + // 0.cs(4,19): error CS1950: The best overloaded Add method 'MyCollection.Add(sbyte)' for the collection initializer has some invalid arguments // MyCollection x = [uint.MaxValue]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[uint.MaxValue]").WithArguments("uint", "MyCollection.Add(sbyte)").WithLocation(4, 18), - // 0.cs(4,18): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "uint.MaxValue").WithArguments("MyCollection.Add(sbyte)").WithLocation(4, 19), + // 0.cs(4,19): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' // MyCollection x = [uint.MaxValue]; - Diagnostic(ErrorCode.ERR_BadArgType, "[uint.MaxValue]").WithArguments("1", "uint", "sbyte").WithLocation(4, 18) + Diagnostic(ErrorCode.ERR_BadArgType, "uint.MaxValue").WithArguments("1", "uint", "sbyte").WithLocation(4, 19) ); } @@ -27167,12 +27719,9 @@ class Collection : IEnumerable """; CreateCompilation(source).VerifyEmitDiagnostics( - // (4,16): error CS9215: Collection expression type must have an applicable instance or extension method 'Add' that can be called with an argument of iteration type 'object'. The best overloaded method is 'Collection.Add(I1)'. - // Collection c = [new C()]; - Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new C()]").WithArguments("object", "Collection.Add(I1)").WithLocation(4, 16), - // (4,16): error CS1503: Argument 1: cannot convert from 'object' to 'I1' + // (4,17): error CS0121: The call is ambiguous between the following methods or properties: 'Collection.Add(I1)' and 'Collection.Add(I2)' // Collection c = [new C()]; - Diagnostic(ErrorCode.ERR_BadArgType, "[new C()]").WithArguments("1", "object", "I1").WithLocation(4, 16)); + Diagnostic(ErrorCode.ERR_AmbigCall, "new C()").WithArguments("Collection.Add(I1)", "Collection.Add(I2)").WithLocation(4, 17)); } [Fact] @@ -33391,714 +33940,3816 @@ .locals init (int V_0, IL_00a1: call "void CollectionExtensions.Report(object, bool)" IL_00a6: ret } - """); + """); + } + + [Fact] + public void TypeInference_LambdaReturn() + { + var source = """ + #nullable enable + using System; + class Program + { + static void Main() + { + object x = new object(); + object y = null; + object[] z = new[] { x }; + F(z, () => [x]); + F(z, () => [y]); + } + static void F(T t, Func f) { } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,20): warning CS8600: Converting null literal or possible null value to non-nullable type. + // object y = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 20), + // (11,21): warning CS8601: Possible null reference assignment. + // F(z, () => [y]); + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y").WithLocation(11, 21)); + } + + [Fact] + public void TargetTypedElement_PublicAPI_List() + { + var source = """ + using System.Collections.Generic; + class C + { + static void Main() + { + List items = [new()]; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_Array() + { + var source = """ + class C + { + static void Main() + { + object[] items = [new()]; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_Span() + { + var source = """ + using System; + + class C + { + static void Main() + { + Span items = [new()]; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_ReadOnlySpan() + { + var source = """ + using System; + + class C + { + static void Main() + { + ReadOnlySpan items = [new()]; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.ReadOnlySpan) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_ImmutableArray() + { + var source = """ + using System.Collections.Immutable; + + class C + { + static void Main() + { + ImmutableArray items = [new()]; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_IEnumerableT() + { + var source = """ + using System.Collections.Generic; + + class C + { + static void Main() + { + IEnumerable items = [new()]; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [Fact] + public void TargetTypedElement_PublicAPI_ImplementsIEnumerable() + { + var source = """ + using System.Collections; + + class C + { + static void Main() + { + MyCollection items = [new()]; + } + } + + class MyCollection : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null!; + public void Add(object obj) { } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var info = model.GetSymbolInfo(node); + Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + + model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[new()]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') + Arguments(0) + Initializer: + null + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_01() + { + string source = """ + using System; + using System.Collections.Generic; + static class Extensions + { + public static void Add(this ICollection collection, params T[] elements) + { + foreach (T element in elements) + collection.Add(element); + } + } + class Program + { + static Dictionary CreateDictionary(ICollection> collection) + { + return /**/[..collection]/**/; + } + static void Main() + { + var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; + var d = CreateDictionary(v); + foreach (var kvp in d) + Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), "); + + verifier.VerifyIL("Extensions.Add(this System.Collections.Generic.ICollection, params T[])", """ + { + // Code size 32 (0x20) + .maxstack 2 + .locals init (T[] V_0, + int V_1, + T V_2) //element + IL_0000: ldarg.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.0 + IL_0003: stloc.1 + IL_0004: br.s IL_0019 + IL_0006: ldloc.0 + IL_0007: ldloc.1 + IL_0008: ldelem "T" + IL_000d: stloc.2 + IL_000e: ldarg.0 + IL_000f: ldloc.2 + IL_0010: callvirt "void System.Collections.Generic.ICollection.Add(T)" + IL_0015: ldloc.1 + IL_0016: ldc.i4.1 + IL_0017: add + IL_0018: stloc.1 + IL_0019: ldloc.1 + IL_001a: ldloc.0 + IL_001b: ldlen + IL_001c: conv.i4 + IL_001d: blt.s IL_0006 + IL_001f: ret + } + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') + Elements(1): + ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') + Operand: + IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsCollection_01() + { + string source = """ + using System; + using System.Collections.Generic; + static class Extensions + { + public static void Add(this ICollection collection, params IEnumerable elements) + { + foreach (T element in elements) + collection.Add(element); + } + } + class Program + { + static Dictionary CreateDictionary(ICollection> collection) + { + return /**/[..collection]/**/; + } + static void Main() + { + var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; + var d = CreateDictionary(v); + foreach (var kvp in d) + Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), ").VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') + Elements(1): + ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') + Operand: + IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(params T[] x) => _list.AddRange(x); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_03() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + internal void __AddRange(T[] x) { _list.AddRange(x); } + } + static class Extensions + { + public static void Add(this MyCollection c, params T[] x) { c.__AddRange(x); } + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [Fact] + public void Add_ParamsArray_04() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(T x, params T[] y) => _list.Add(x); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [Fact] + public void Add_ParamsArray_05() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + internal void __Add(T x) { _list.Add(x); } + } + static class Extensions + { + public static void Add(this MyCollection c, T x, params T[] y) { c.__Add(x); } + } + class Program + { + static void Main() + { + int x = 1; + MyCollection y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') + ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_06() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(params MyCollection[] x) + { + Console.Write("Add: "); + x.Report(); + Console.WriteLine(); + _list.AddRange(x); + } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + MyCollection x = []; + MyCollection[] y = []; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: [[]], + [[]], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') + ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection x = /**/[[]]/**/; + x.Report(); + } + } + """; + + comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: [], + [], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection[], IsImplicit) (Syntax: '[]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection[]) (Syntax: '[]') + Elements(0) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_07() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + struct MyCollection : IEnumerable + { + private List _list; + public void Add(params MyCollection?[] x) => GetList().AddRange(x); + public IEnumerator GetEnumerator() => GetList().GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private List GetList() => _list ??= new(); + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + MyCollection x = []; + MyCollection[] y = []; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[[]], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?, IsImplicit) (Syntax: 'x') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') + ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (ImplicitNullable) + """); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection? x = /**/[[]]/**/; + x.Value.Report(); + } + } + """; + + comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "[], "); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') + Elements(1): + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?[], IsImplicit) (Syntax: '[]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection?[]) (Syntax: '[]') + Elements(0) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_08() + { + string sourceA = """ + using System; + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list = new(); + public void Add(params object[] x) + { + Console.Write("Add: "); + foreach (var i in x) + Console.Write("{0}, ", i); + Console.WriteLine(); + _list.AddRange(x); + } + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: 1, + Add: 2, + Add: 3, + [1, 2, 3], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') + ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + + string sourceB2 = """ + class Program + { + static void Main() + { + object[] x = [1]; + object[][] y = [[2, 3]]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: """ + Add: 1, + Add: 2, 3, + [1, 2, 3], + """); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'x') + ISpreadOperation (ElementType: System.Object[]) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[][]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + [Fact] + public void Add_ParamsArray_09() + { + string sourceA1 = """ + public abstract class MyCollectionBase + { + public abstract void Add(object[] x); + } + """; + string assemblyName = GetUniqueName(); + var comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(1, 0, 0, 0)), sourceA1, references: TargetFrameworkUtil.StandardReferences); + var refA1 = comp.EmitToImageReference(); + + string sourceB = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : MyCollectionBase, IEnumerable + { + private List _list = new(); + public override void Add(object[] x) => _list.AddRange(x); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + """; + comp = CreateCompilation(sourceB, references: [refA1]); + var refB = comp.EmitToImageReference(); + + string sourceA2 = """ + public abstract class MyCollectionBase + { + public abstract void Add(params object[] x); + } + """; + comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(2, 0, 0, 0)), sourceA2, references: TargetFrameworkUtil.StandardReferences); + var refA2 = comp.EmitToImageReference(); + + string sourceC = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = /**/[x, ..y]/**/; + z.Report(); + } + } + """; + + comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refA2, refB], options: TestOptions.ReleaseExe); + comp.VerifyEmitDiagnostics(); + + VerifyOperationTreeForTest(comp, + """ + ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') + Elements(2): + ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') + ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') + Operand: + ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') + ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + """); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Element { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + ElementCollection c = [new Element(), null]; + c.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Base { } + class Element : Base { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + ElementCollection c = [new Element(), null]; + c.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/71240")] + [Fact] + public void AddMethod_Derived_03() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + + class Sample : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { if (t is object[] o) _list.Add(o); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + Sample s = [["a"], ["b"], ["c"]]; + s.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[[a], [b], [c]], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + Sample s = ["a", null]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,29): error CS0029: Cannot implicitly convert type 'string' to 'object[]' + // Sample s = ["a", null]; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""a""").WithArguments("string", "object[]").WithLocation(5, 29)); + } + + [Fact] + public void AddMethod_Accessibility_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + partial class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private void Add(int i) { _list.Add(i is T t ? t : default); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB1]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS0122: 'MyCollection.Add(int)' is inaccessible due to its protection level + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadAccess, "[x, ..y]").WithArguments("MyCollection.Add(int)").WithLocation(7, 34)); + + string sourceB2 = """ + partial class MyCollection + { + public static void Run() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + class Program + { + static void Main() + { + MyCollection.Run(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_Accessibility_02() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + internal void __AddInternal(int i) { _list.Add(i is T t ? t : default); } + } + internal static class Extensions + { + public static void Add(this MyCollection c, int i) { c.__AddInternal(i); } + } + """; + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + var comp = CreateCompilation(sourceA); + var refA = comp.ToMetadataReference(); + + comp = CreateCompilation([sourceB, s_collectionExtensions], references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(7, 34)); + } + + [Fact] + public void AddMethod_Accessibility_03() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollectionBase : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + protected void Add(T t) { _list.Add(t); } + } + class MyCollection : MyCollectionBase + { + internal void __AddInternal(T t) { Add(t); } + } + """; + string sourceB = """ + static class Extensions + { + public static void Add(this MyCollection c, T t) { c.__AddInternal(t); } + } + """; + string sourceC = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + var comp = CreateCompilation([sourceA, sourceC, s_collectionExtensions]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS0122: 'MyCollectionBase.Add(object)' is inaccessible due to its protection level + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadAccess, "[x, ..y]").WithArguments("MyCollectionBase.Add(object)").WithLocation(7, 34)); + } + + [Fact] + public void AddMethod_Overloads() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(string s) { _list.Add(s is T t ? t : default); } + public void Add(int i) { _list.Add(i is T t ? t : default); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + string x = "1"; + string[] y = ["2", "3"]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB3 = """ + class Program + { + static void Main() + { + int? x = 1; + int?[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB3]); + comp.VerifyEmitDiagnostics( + // (7,33): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(string)").WithLocation(7, 33), + // (7,33): error CS1503: Argument 1: cannot convert from 'int?' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int?", "string").WithLocation(7, 33), + // (7,36): error CS1503: Argument 1: cannot convert from 'int?' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int?", "string").WithLocation(7, 36), + // (7,38): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(string)").WithLocation(7, 38)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_01(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add({{refKind}} T t) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_02(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add({{refKind}} T t) { t = default; _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,32): error CS1954: The best overloaded method match 'MyCollection.Add(ref int?)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[x, ..y, null]").WithArguments("MyCollection.Add(" + refKind + " int?)").WithLocation(15, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_03(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + interface IA { } + interface IB { } + interface IC { } + class C : IA, IB, IC + { + private readonly int _i; + public C(int i) { _i = i; } + public override string ToString() => _i.ToString(); + public static implicit operator C(int i) => new(i); + } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(out IA a) => throw null; + public void Add(ref IB b) => throw null; + public void Add({{refKind}} IC c) { _list.Add(c); } + } + class Program + { + static void Main() + { + C x = 1; + C[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_01(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_Extension_02(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { t = default; collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS1954: The best overloaded method match 'Extensions.Add(MyCollection, ref T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[x, ..y, null]").WithArguments("Extensions.Add(MyCollection, " + refKind + " T)").WithLocation(19, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_03(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + ref struct R { } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + public void Add(R r) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_Extension_04(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + ref struct R { } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + public void Add(R r) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { t = default; collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (21,33): error CS1950: The best overloaded Add method 'MyCollection.Add(R)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(R)").WithLocation(21, 33), + // (21,33): error CS1503: Argument 1: cannot convert from 'int' to 'R' + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "R").WithLocation(21, 33), + // (21,36): error CS1503: Argument 1: cannot convert from 'int' to 'R' + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "R").WithLocation(21, 36), + // (21,38): error CS1950: The best overloaded Add method 'MyCollection.Add(R)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(R)").WithLocation(21, 38), + // (21,41): error CS1950: The best overloaded Add method 'MyCollection.Add(R)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("MyCollection.Add(R)").WithLocation(21, 41), + // (21,41): error CS1503: Argument 1: cannot convert from '' to 'R' + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "R").WithLocation(21, 41)); + } + + [Theory] + [InlineData("out")] + [InlineData("ref")] + public void AddMethod_ByRef_Extension_05(string refKind) + { + string source = $$""" + using N; + using System.Collections; + using System.Collections.Generic; + ref struct R { } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + public void Add(R r) => throw null; + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} T t) { t = default; collection.__AddInternal(t); } + } + namespace N + { + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_06(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + interface IA { } + interface IB { } + interface IC { } + class C : IA, IB, IC + { + private readonly int _i; + public C(int i) { _i = i; } + public override string ToString() => _i.ToString(); + public static implicit operator C(int i) => new(i); + } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(out IA a) => throw null; + public void Add(ref IB b) => throw null; + internal void __AddInternal(IC c) { _list.Add(c); } + } + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} IC c) { collection.__AddInternal(c); } + } + class Program + { + static void Main() + { + C x = 1; + C[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_ByRef_Extension_07(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + using N; + interface IA { } + interface IB { } + interface IC { } + class C : IA, IB, IC + { + private readonly int _i; + public C(int i) { _i = i; } + public override string ToString() => _i.ToString(); + public static implicit operator C(int i) => new(i); + } + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(IC c) { _list.Add(c); } + } + static class Extensions + { + public static void Add(this MyCollection collection, out IA a) => throw null; + public static void Add(this MyCollection collection, ref IB b) => throw null; + } + namespace N + { + static class Extensions + { + public static void Add(this MyCollection collection, {{refKind}} IC c) { collection.__AddInternal(c); } + } + } + class Program + { + static void Main() + { + C x = 1; + C[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add() { _list.Add(default); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,32): error CS1501: No overload for method 'Add' takes 1 arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgCount, "[x, ..y, null]").WithArguments("Add", "1").WithLocation(15, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_02A(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t, {{refKind}} int x = 1, {{refKind}} int y = 2) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_02B() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T t, int x = 1); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T t, int x) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'MyCollection.Add(int?, int)' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("x", "MyCollection.Add(int?, int)").WithLocation(20, 40)); + } + + [Fact] + public void AddMethod_02C() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T t, int x); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T t, int x = 1) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [InlineData("")] + [InlineData("in ")] + [InlineData("ref readonly ")] + public void AddMethod_03A(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t, {{refKind}} int x, {{refKind}} int y = 2) { _list.Add(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + if (refKind == "ref readonly ") + { + comp.VerifyEmitDiagnostics( + // (7,69): warning CS9200: A default value is specified for 'ref readonly' parameter 'y', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. + // public void Add(T t, ref readonly int x, ref readonly int y = 2) { _list.Add(t); } + Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "2").WithArguments("y").WithLocation(7, 69), + // (15,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 32)); + } + else + { + comp.VerifyEmitDiagnostics( + // (15,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 32)); + } + } + + [Theory] + [CombinatorialData] + public void AddMethod_03B(bool useOut) + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // public void Add(T t, ref int index = 0) => throw null; + // } + string sourceA = $$""" + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + .method public instance void Add(!T t, {{(useOut ? "[out]" : "")}} [opt] int32& index) + { + .param [2] = int32(0x00000000) + ldnull + ret + } + } + """; + var refA = CompileIL(sourceA); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 31), + // (8,39): error CS7036: There is no argument given that corresponds to the required parameter 'index' of 'MyCollection.Add(int, ref int)' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("index", $"MyCollection.Add(int, {(useOut ? "out" : "ref")} int)").WithLocation(8, 39)); + } + + [Fact] + public void AddMethod_04A() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(params T[] args); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T[] args) + { + if (args is null) return; + _list.AddRange(args); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection x = new() { (int?)null, null }; + MyCollection y = [(int?)null, null]; + x.Report(); + y.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[null], [null], "); + } + + [Fact] + public void AddMethod_04B() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T[] args); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(params T[] args) + { + if (args is null) return; + _list.AddRange(args); + } + } + """; + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB]); + comp.VerifyEmitDiagnostics( + // (7,33): error CS1950: The best overloaded Add method 'MyCollection.Add(int?[])' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(int?[])").WithLocation(7, 33), + // (7,33): error CS1503: Argument 1: cannot convert from 'int' to 'int?[]' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "int?[]").WithLocation(7, 33), + // (7,36): error CS1503: Argument 1: cannot convert from 'int' to 'int?[]' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "int?[]").WithLocation(7, 36), + // (7,38): error CS1950: The best overloaded Add method 'MyCollection.Add(int?[])' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(int?[])").WithLocation(7, 38)); + } + + [Fact] + public void AddMethod_05A() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, params T[] y); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, T[] y) { _list.Add(x); _list.AddRange(y); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_05B() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, T[] y); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, params T[] y) { _list.Add(x); _list.AddRange(y); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + + [Fact] + public void AddMethod_06A() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, T y = default, params T[] z); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, T y, T[] z) { _list.Add(x); _list.Add(y); _list.AddRange(z); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'MyCollection.Add(int?, int?, params int?[])' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("y", "MyCollection.Add(int?, int?, params int?[])").WithLocation(20, 40)); + } + + [Fact] + public void AddMethod_06B() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + public abstract void Add(T x, T y, T[] z); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public override void Add(T x, T y = default, params T[] z) { _list.Add(x); _list.Add(y); _list.AddRange(z); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS7036: There is no argument given that corresponds to the required parameter 'z' of 'MyCollection.Add(int?, int?, int?[])' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("z", "MyCollection.Add(int?, int?, int?[])").WithLocation(20, 40)); + } + + [Fact] + public void AddMethod_07() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(string s) { } + internal void __AddInternal(T t) { _list.Add(t); } + } + namespace N + { + internal static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + } + """; + var comp = CreateCompilation(sourceA); + var refA = comp.EmitToImageReference(); + + string sourceB = """ + using N; + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + comp = CreateCompilation([sourceB, s_collectionExtensions], references: [refA]); + comp.VerifyEmitDiagnostics( + // (8,32): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(string)").WithLocation(8, 32), + // (8,32): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "string").WithLocation(8, 32), + // (8,35): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "string").WithLocation(8, 35), + // (8,37): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(string)").WithLocation(8, 37)); + } + + [Fact] + public void AddMethod_08() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(string s) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, int i) { collection.__AddInternal(i); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB1, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceB2, sourceA]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(string)").WithLocation(7, 35), + // (7,35): error CS1503: Argument 1: cannot convert from 'object' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "object", "string").WithLocation(7, 35), + // (7,38): error CS1503: Argument 1: cannot convert from 'object' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "object", "string").WithLocation(7, 38), + // (7,40): error CS1950: The best overloaded Add method 'MyCollection.Add(string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(string)").WithLocation(7, 40)); + } + + [Fact] + public void AddMethod_09() + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // public void Add(T x, params T y) => throw null; + // } + string sourceA = $$""" + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + .method public instance void Add(!T x, !T y) + { + .param [2] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + ret + } + } + """; + var refA = CompileIL(sourceA); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 31), + // (8,39): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'MyCollection.Add(int, params int)' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "x").WithArguments("y", "MyCollection.Add(int, params int)").WithLocation(8, 39)); + } + + [Fact] + public void AddMethod_ParamCollectionAttribute_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T x, params List y) + { + _list.Add(x); + foreach (var i in y) + _list.Add(i); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB1, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_ParamCollectionAttribute_02() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(U x, params MyCollection y) where U : T + { + _list.Add(x); + foreach (var i in y) + _list.Add(i); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB1, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_ParamCollectionAttribute_03() + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // public void Add(object x, [ParamCollection] object y) => throw null; + // } + string sourceA = $$""" + .class public System.Runtime.CompilerServices.ParamCollectionAttribute extends [mscorlib]System.Attribute + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + } + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + .method public instance void Add(object x, object y) + { + .param [2] + .custom instance void System.Runtime.CompilerServices.ParamCollectionAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + ret + } + } + """; + var refA = CompileIL(sourceA); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 31)); + } + + [Fact] + public void AddMethod_Extension_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection) { collection.__AddInternal(default); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS1501: No overload for method 'Add' takes 1 arguments + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_BadArgCount, "[x, ..y, null]").WithArguments("Add", "1").WithLocation(19, 32)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_Extension_02(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t, {{refKind}} int x = 1, {{refKind}} int y = 2) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref readonly")] + public void AddMethod_Extension_03(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t, {{refKind}} int x, {{refKind}} int y = 2) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } + } + """; + var comp = CreateCompilation(source); + if (refKind == "ref readonly") + { + comp.VerifyEmitDiagnostics( + // (11,110): warning CS9200: A default value is specified for 'ref readonly' parameter 'y', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. + // public static void Add(this MyCollection collection, T t, ref readonly int x, ref readonly int y = 2) { collection.__AddInternal(t); } + Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "2").WithArguments("y").WithLocation(11, 110), + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + else if (refKind == "in") + { + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + else + { + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 32)); + } + } + + [Fact] + public void AddMethod_Extension_04() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, params T[] args) + { + if (args is null) return; + foreach (var a in args) + collection.__AddInternal(a); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection x = new() { (int?)null, null }; + MyCollection y = [(int?)null, null]; + x.Report(); + y.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[null], [null], "); + } + + [Fact] + public void AddMethod_Extension_05() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T x, params T[] y) + { + collection.__AddInternal(x); + foreach (var a in y) + collection.__AddInternal(a); + } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, null], "); + } + + [Fact] + public void AddMethod_Extension_06() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T x, T y = default, params T[] z) + { + collection.__AddInternal(x); + collection.__AddInternal(y); + foreach (var a in z) + collection.__AddInternal(a); + } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, null, 2, null, 3, null], "); + } + + [Fact] + public void AddMethod_Extension_07() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + public static class Extensions + { + public static void Add(this MyCollection collection, string s) { } + } + namespace N + { + internal static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + } + """; + var comp = CreateCompilation(sourceA); + var refA = comp.EmitToImageReference(); + + string sourceB = """ + using N; + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceB, sourceA, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + comp = CreateCompilation([sourceB, s_collectionExtensions], references: [refA]); + comp.VerifyEmitDiagnostics( + // (8,32): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(8, 32), + // (8,32): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "string").WithLocation(8, 32), + // (8,35): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "string").WithLocation(8, 35), + // (8,37): error CS1950: The best overloaded Add method 'Extensions.Add(MyCollection, string)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions.Add(MyCollection, string)").WithLocation(8, 37)); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref")] + [InlineData("ref readonly")] + public void AddMethod_Extension_08A(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + struct MyCollection : IEnumerable + { + private List _list; + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + internal void __AddInternal(T t) { GetList().Add(t); } + private List GetList() => _list ??= new(); + } + static class Extensions + { + public static void Add(this {{refKind}} MyCollection collection, T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + string expectedOutput = (refKind == "ref") ? "[1, 2, 3], " : "[], "; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: expectedOutput); + } + + [Theory] + [InlineData("")] + [InlineData("in")] + [InlineData("ref")] + [InlineData("ref readonly")] + public void AddMethod_Extension_08B(string refKind) + { + string source = $$""" + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private List _list; + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + internal void __AddInternal(T t) { GetList().Add(t); } + private List GetList() => _list ??= new(); + } + static class Extensions + { + public static void Add(this {{refKind}} MyCollection collection, T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); + switch (refKind) + { + case "": + CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); + break; + case "in": + case "ref readonly": + comp.VerifyEmitDiagnostics( + // (12,24): error CS8338: The first 'in' or 'ref readonly' parameter of the extension method 'Add' must be a concrete (non-generic) value type. + // public static void Add(this in MyCollection collection, T t) { collection.__AddInternal(t); } + Diagnostic(ErrorCode.ERR_InExtensionMustBeValueType, "Add").WithArguments("Add").WithLocation(12, 24), + // (20,31): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(20, 31)); + break; + case "ref": + comp.VerifyEmitDiagnostics( + // (12,24): error CS8337: The first parameter of a 'ref' extension method 'Add' must be a value type or a generic type constrained to struct. + // public static void Add(this ref MyCollection collection, T t) { collection.__AddInternal(t); } + Diagnostic(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, "Add").WithArguments("Add").WithLocation(12, 24), + // (20,31): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(20, 31)); + break; + } + } + + [Fact] + public void AddMethod_Extension_09() + { + // public struct MyCollection : IEnumerable + // { + // IEnumerator IEnumerable.GetEnumerator() => null; + // } + // public static class Extensions + // { + // public static void Add(this out MyCollection collection, T t) => throw null; + // } + string sourceA = """ + .assembly extern mscorlib { .ver 4:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89) } + .assembly '<>' + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + } + .class public sealed MyCollection`1 + extends [mscorlib]System.ValueType + implements [mscorlib]System.Collections.IEnumerable + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } + .method private instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() + { + .override [mscorlib]System.Collections.IEnumerable::GetEnumerator + ldnull + ret + } + } + .class public abstract sealed Extensions + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + .method public hidebysig static void Add([out] valuetype MyCollection`1& collection, !!T t) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) + ldnull + throw + } + } + """; + var refA = CompileIL(sourceA, prependDefaultHeader: false); + + string sourceB = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation(sourceB, references: [refA]); + comp.VerifyEmitDiagnostics( + // (7,31): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(7, 31)); + } + + [Fact] + public void AddMethod_Extension_10_WrongThisType() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this string collection, int x) => throw null; + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS1929: 'MyCollection' does not contain a definition for 'Add' and the best extension method overload 'Extensions.Add(string, int)' requires a receiver of type 'string' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "[x, ..y]").WithArguments("MyCollection", "Add", "Extensions.Add(string, int)", "string").WithLocation(19, 32), + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,32): error CS1929: 'MyCollection' does not contain a definition for 'Add' and the best extension method overload 'Extensions.Add(string, int)' requires a receiver of type 'string' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new() { x }").WithArguments("MyCollection", "Add", "Extensions.Add(string, int)", "string").WithLocation(20, 32), + // (20,40): error CS1950: The best overloaded Add method 'Extensions.Add(string, int)' for the collection initializer has some invalid arguments + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add(string, int)").WithLocation(20, 40) + ); + } + + [Fact] + public void AddMethod_Extension_11_WrongThisType() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this IEnumerable collection, int x) => throw null; + } + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(19, 32), + // (20,40): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "x").WithArguments("MyCollection", "Add").WithLocation(20, 40) + ); + } + + [Fact] + public void AddMethod_Extension_12_WrongThisType() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (7,39): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 39)); + } + + [Fact] + public void AddMethod_Extension_13_WrongThisType() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this IEnumerable collection, T t) { ((MyCollection)collection).__AddInternal(t); } + } + """; + string sourceB = """ + class Program + { + static void Main() + { + string x = "1"; + string[] y = ["2", "3"]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } + + [Fact] + public void AddMethod_Extension_14_ConstraintsViolated() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, params T[] args) + where T : struct + { + if (args is null) return; + foreach (var a in args) + collection.__AddInternal(a); + } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + MyCollection w = new() { x }; + } + } + """; + CreateCompilation([sourceA, sourceB1, s_collectionExtensions]).VerifyDiagnostics( + // (7,32): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y]").WithArguments("MyCollection").WithLocation(7, 32), + // (9,40): error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Extensions.Add(MyCollection, params T[])' + // MyCollection w = new() { x }; + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "x").WithArguments("Extensions.Add(MyCollection, params T[])", "T", "int?").WithLocation(9, 40) + ); } [Fact] - public void TypeInference_LambdaReturn() + public void AddMethod_Extension_15_Dynamic() { - var source = """ - #nullable enable - using System; + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this dynamic d, T t) { d.__AddInternal(t); } + } + """; + string sourceB = """ class Program { static void Main() { - object x = new object(); - object y = null; - object[] z = new[] { x }; - F(z, () => [x]); - F(z, () => [y]); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; } - static void F(T t, Func f) { } } """; - var comp = CreateCompilation(source); + var comp = CreateCompilation([sourceA, sourceB]); comp.VerifyEmitDiagnostics( - // (8,20): warning CS8600: Converting null literal or possible null value to non-nullable type. - // object y = null; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 20), - // (11,21): warning CS8601: Possible null reference assignment. - // F(z, () => [y]); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y").WithLocation(11, 21)); + // (11,36): error CS1103: The first parameter of an extension method cannot be of type 'dynamic' + // public static void Add(this dynamic d, T t) { d.__AddInternal(t); } + Diagnostic(ErrorCode.ERR_BadTypeforThis, "dynamic").WithArguments("dynamic").WithLocation(11, 36)); } - [Fact] - public void TargetTypedElement_PublicAPI_List() + [WorkItem("https://github.com/dotnet/roslyn/issues/72769")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/72769")] + public void AddMethod_RefOmittedArguments() { - var source = """ - using System.Collections.Generic; - class C + string sourceA = """ + using System; + using System.Collections; + using System.Runtime.InteropServices; + + [ComImport] + [Guid("5CDF1E39-B461-4A9B-9359-1D6F7DECE1B3")] + class MyCollection : IEnumerable + { + extern IEnumerator IEnumerable.GetEnumerator(); + } + + static class Extensions + { + public static void Add(this MyCollection collection, ref T x) => throw null; + } + """; + string sourceB = """ + class Program { static void Main() { - List items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + MyCollection w = new() { x }; } } """; - - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + var comp = CreateCompilation([sourceA, sourceB]); + // https://github.com/dotnet/roslyn/issues/72769: VerifyEmitDiagnostics() results in Debug.Assert + // failures in LocalRewriter.MakeCollectionInitializer() and GetEffectiveArgumentRefKinds(). + comp.VerifyEmitDiagnostics(); } [Fact] - public void TargetTypedElement_PublicAPI_Array() + public void AddMethod_Base() { - var source = """ - class C + string source = """ + using System.Collections; + using System.Collections.Generic; + abstract class MyCollectionBase + { + protected abstract void __AddInternal(T t); + public void Add(T t) => __AddInternal(t); + } + class MyCollection : MyCollectionBase, IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + protected override void __AddInternal(T t) { _list.Add(t); } + } + class Program { static void Main() { - object[] items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } """; - - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } [Fact] - public void TargetTypedElement_PublicAPI_Span() + public void AddMethod_Derived() { - var source = """ - using System; - - class C + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + protected void __AddInternal(T t) { _list.Add(t); } + } + class MyCollectionDerived : MyCollection + { + public void Add(T t) => __AddInternal(t); + } + class Program { static void Main() { - Span items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollectionDerived z = [x, ..y]; + z.Report(); } } """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics(); + [Theory] + [InlineData("class")] + [InlineData("struct")] + public void AddMethod_ExplicitImplementation(string structOrClass) + { + string sourceA = $$""" + using System.Collections; + using System.Collections.Generic; + interface IAdd + { + void Add(T t); + } + {{structOrClass}} MyCollection : IAdd, IEnumerable + { + private List _list; + IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); + void IAdd.Add(T t) { GetList().Add(t); } + private List GetList() => _list ??= new(); + } + """; + string sourceB = """ + class Program + { + static void Main() + { + object x = 1; + object[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + string sourceC = $$""" + static class Extensions + { + public static void Add(this {{(structOrClass == "struct" ? "ref" : "")}} T collection, U u) + where T : {{structOrClass}}, IAdd + { + collection.Add(u); + } + } + """; - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensions]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[x, ..y]").WithArguments("MyCollection", "Add").WithLocation(7, 34)); - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } [Fact] - public void TargetTypedElement_PublicAPI_ReadOnlySpan() + public void AddMethod_Static() { - var source = """ - using System; - - class C + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public static void Add(T t) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + """; + string sourceB = """ + class Program { static void Main() { - ReadOnlySpan items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } """; + string sourceC = """ + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + """; - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics(); + var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensions]); + comp.VerifyEmitDiagnostics( + // (7,34): error CS1921: The best overloaded method match for 'MyCollection.Add(object)' has wrong signature for the initializer element. The initializable Add must be an accessible instance method. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_InitializerAddHasWrongSignature, "[x, ..y]").WithArguments("MyCollection.Add(object)").WithLocation(7, 34)); - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.ReadOnlySpan) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + [Fact] + public void AddMethod_Generic_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(U u) { _list.Add(u is T t ? t : default); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + MyCollection z = [null]; + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,35): error CS0411: The type arguments for method 'MyCollection.Add(U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("MyCollection.Add(U)").WithLocation(5, 35)); } [Fact] - public void TargetTypedElement_PublicAPI_ImmutableArray() + public void AddMethod_Generic_02() { - var source = """ - using System.Collections.Immutable; - - class C + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { _list.Add(t); } + } + class Program { static void Main() { - ImmutableArray items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; - - var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray System.Collections.Immutable.ImmutableArray.Create(System.ReadOnlySpan items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (15,34): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 34)); } [Fact] - public void TargetTypedElement_PublicAPI_IEnumerableT() + public void AddMethod_Generic_03() { - var source = """ + string source = """ + using System.Collections; using System.Collections.Generic; - - class C + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t, U u = default) { _list.Add(t); } + } + class Program { static void Main() { - IEnumerable items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + comp.VerifyEmitDiagnostics( + // (15,34): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(15, 34)); } [Fact] - public void TargetTypedElement_PublicAPI_ImplementsIEnumerable() + public void AddMethod_Generic_04() { - var source = """ + string sourceA = """ using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, U u) { collection.__AddInternal(u is T t ? t : default); } + } + """; - class C + string sourceB1 = """ + class Program { static void Main() { - MyCollection items = [new()]; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); - class MyCollection : IEnumerable + string sourceB2 = """ + class Program { - IEnumerator IEnumerable.GetEnumerator() => throw null!; - public void Add(object obj) { } + static void Main() + { + MyCollection z = [null]; + } } """; - - var comp = CreateCompilation(source); - comp.VerifyDiagnostics(); - - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var node = tree.GetRoot().DescendantNodes().OfType().Single(); - var info = model.GetSymbolInfo(node); - Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); - - model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType().Single(), """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[new()]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') - Arguments(0) - Initializer: - null - """); + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,35): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, U)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("Extensions.Add(MyCollection, U)").WithLocation(5, 35)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_01() + public void AddMethod_Generic_05() { string source = """ - using System; + using System.Collections; using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } static class Extensions { - public static void Add(this ICollection collection, params T[] elements) - { - foreach (T element in elements) - collection.Add(element); - } + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } class Program { - static Dictionary CreateDictionary(ICollection> collection) - { - return /**/[..collection]/**/; - } static void Main() { - var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; - var d = CreateDictionary(v); - foreach (var kvp in d) - Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; - - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - var verifier = CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), "); - - verifier.VerifyIL("Extensions.Add(this System.Collections.Generic.ICollection, params T[])", """ - { - // Code size 32 (0x20) - .maxstack 2 - .locals init (T[] V_0, - int V_1, - T V_2) //element - IL_0000: ldarg.1 - IL_0001: stloc.0 - IL_0002: ldc.i4.0 - IL_0003: stloc.1 - IL_0004: br.s IL_0019 - IL_0006: ldloc.0 - IL_0007: ldloc.1 - IL_0008: ldelem "T" - IL_000d: stloc.2 - IL_000e: ldarg.0 - IL_000f: ldloc.2 - IL_0010: callvirt "void System.Collections.Generic.ICollection.Add(T)" - IL_0015: ldloc.1 - IL_0016: ldc.i4.1 - IL_0017: add - IL_0018: stloc.1 - IL_0019: ldloc.1 - IL_001a: ldloc.0 - IL_001b: ldlen - IL_001c: conv.i4 - IL_001d: blt.s IL_0006 - IL_001f: ret - } - """); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') - Elements(1): - ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') - Operand: - IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (19,34): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[x, ..y, null]").WithArguments("MyCollection").WithLocation(19, 34)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsCollection_01() + public void AddMethod_Generic_06() { - string source = """ - using System; + string sourceA = """ + using System.Collections; using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } static class Extensions { - public static void Add(this ICollection collection, params IEnumerable elements) - { - foreach (T element in elements) - collection.Add(element); - } + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } + """; + + string sourceB1 = """ class Program { - static Dictionary CreateDictionary(ICollection> collection) + static void Main() { - return /**/[..collection]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } + } + """; + CompileAndVerify([sourceA, sourceB1, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + + string sourceB2 = """ + class Program + { static void Main() { - var v = new KeyValuePair[] { new("a", "b"), new("c", "d") }; - var d = CreateDictionary(v); - foreach (var kvp in d) - Console.Write("({0}, {1}), ", kvp.Key, kvp.Value); + MyCollection z = [null]; } } """; - - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); - CompileAndVerify(comp, expectedOutput: "(a, b), (c, d), ").VerifyDiagnostics(); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.Dictionary..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary) (Syntax: '[..collection]') - Elements(1): - ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair) (OperationKind.Spread, Type: null) (Syntax: '..collection') - Operand: - IParameterReferenceOperation: collection (OperationKind.ParameterReference, Type: System.Collections.Generic.ICollection>) (Syntax: 'collection') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,35): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("Extensions.Add(MyCollection, T)").WithLocation(5, 35)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_02() + public void AddMethod_Generic_07() { string source = """ using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(params T[] x) => _list.AddRange(x); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T x, U y = default, T z = default) { collection.__AddInternal(x is U u ? u : default); } } class Program { static void Main() { int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; - - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_03() + public void AddMethod_Generic_08() { - string source = """ + string sourceA = """ + using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal void __AddRange(T[] x) { _list.AddRange(x); } + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(Func f) { _list.Add(f); } } - static class Extensions + """; + + string sourceB1 = """ + class Program { - public static void Add(this MyCollection c, params T[] x) { c.__AddRange(x); } + static void Main() + { + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; + } } + """; + var comp = CreateCompilation([sourceA, sourceB1]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0411: The type arguments for method 'MyCollection.Add(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "x").WithArguments("MyCollection.Add(System.Func)").WithLocation(7, 35), + // (7,40): error CS0411: The type arguments for method 'MyCollection.Add(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "y").WithArguments("MyCollection.Add(System.Func)").WithLocation(7, 40), + // (7,43): error CS0411: The type arguments for method 'MyCollection.Add(Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("MyCollection.Add(System.Func)").WithLocation(7, 43)); + + string sourceB2 = """ + using System; class Program { static void Main() { - int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + Func x = _ => 1; + Func[] y = [_ => "2"]; + MyCollection z = [x, ..y]; z.Report(); } } """; - - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[System.Func`2[System.Object,System.Int32], System.Func`2[System.Object,System.String]], "); } [Fact] - public void Add_ParamsArray_04() + public void AddMethod_Generic_09() { - string source = """ + string sourceA = """ + using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(T x, params T[] y) => _list.Add(x); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(Delegate d) { _list.Add(d); } } + static class Extensions + { + public static void Add(this MyCollection collection, Func f) { collection.__AddInternal(f); } + } + """; + + string sourceB1 = """ class Program { static void Main() { int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; - z.Report(); + int[] y = [2, 3]; + MyCollection z = [x, ..y, null]; } } """; + var comp = CreateCompilation([sourceA, sourceB1]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "x").WithArguments("Extensions.Add(MyCollection, System.Func)").WithLocation(7, 35), + // (7,40): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "y").WithArguments("Extensions.Add(MyCollection, System.Func)").WithLocation(7, 40), + // (7,43): error CS0411: The type arguments for method 'Extensions.Add(MyCollection, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // MyCollection z = [x, ..y, null]; + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "null").WithArguments("Extensions.Add(MyCollection, System.Func)").WithLocation(7, 43)); - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + string sourceB2 = """ + using System; + class Program + { + static void Main() + { + Func x = _ => 1; + Func[] y = [_ => "2"]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], expectedOutput: "[System.Func`2[System.Object,System.Int32], System.Func`2[System.Object,System.String]], "); } + // [Obsolete] attribute is ignored when checking for Add for conversion. [Fact] - public void Add_ParamsArray_05() + public void AddMethod_Obsolete_01() { string source = """ + using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - internal void __Add(T x) { _list.Add(x); } + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + [Obsolete("do not use", error: true)] + public void Add(string s) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } } static class Extensions { - public static void Add(this MyCollection c, T x, params T[] y) { c.__Add(x); } + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } class Program { static void Main() { int x = 1; - MyCollection y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; - - var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'x') - ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Boxing) - """); + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] + // [Obsolete] attribute is ignored when checking for Add for conversion. [Fact] - public void Add_ParamsArray_06() + public void AddMethod_Obsolete_02() { - string sourceA = """ + string source = """ + using N; using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(params MyCollection[] x) + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + [Obsolete("do not use", error: true)] + public static void Add(this MyCollection collection, string s) => throw null; + } + namespace N + { + static class Extensions { - Console.Write("Add: "); - x.Report(); - Console.WriteLine(); - _list.AddRange(x); + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - """; - - string sourceB1 = """ class Program { static void Main() { - MyCollection x = []; - MyCollection[] y = []; - MyCollection z = /**/[x, ..y]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: [[]], - [[]], - """); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') - ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); - - string sourceB2 = """ + [Fact] + public void AddMethod_Unsafe_01() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + unsafe public void Add(void* p) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + """; + string sourceB = """ class Program { static void Main() { - MyCollection x = /**/[[]]/**/; - x.Report(); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } } """; - - comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: [], - [], - """); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection[], IsImplicit) (Syntax: '[]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection[]) (Syntax: '[]') - Elements(0) - """); + string sourceC = """ + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + """; + + var comp = CreateCompilation([sourceA, sourceB, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe); + comp.VerifyEmitDiagnostics( + // (7,35): error CS1950: The best overloaded Add method 'MyCollection.Add(void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("MyCollection.Add(void*)").WithLocation(7, 35), + // (7,35): error CS1503: Argument 1: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "void*").WithLocation(7, 35), + // (7,38): error CS1503: Argument 1: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("1", "int", "void*").WithLocation(7, 38), + // (7,40): error CS1950: The best overloaded Add method 'MyCollection.Add(void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("MyCollection.Add(void*)").WithLocation(7, 40)); + + CompileAndVerify([sourceA, sourceB, sourceC, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe, expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_07() + public void AddMethod_Unsafe_02() { string sourceA = """ using System.Collections; using System.Collections.Generic; - struct MyCollection : IEnumerable + class MyCollection : IEnumerable { - private List _list; - public void Add(params MyCollection?[] x) => GetList().AddRange(x); - public IEnumerator GetEnumerator() => GetList().GetEnumerator(); + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - private List GetList() => _list ??= new(); + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions1 + { + unsafe public static void Add(this MyCollection collection, void* p) => throw null; } """; @@ -34107,216 +37758,238 @@ class Program { static void Main() { - MyCollection x = []; - MyCollection[] y = []; - MyCollection z = /**/[x, ..y]/**/; - z.Report(); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; } } """; - - var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[[]], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?, IsImplicit) (Syntax: 'x') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: MyCollection) (Syntax: 'x') - ISpreadOperation (ElementType: MyCollection) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: MyCollection[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (ImplicitNullable) - """); + var comp = CreateCompilation([sourceA, sourceB1, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe); + comp.VerifyEmitDiagnostics( + // (7,35): error CS1950: The best overloaded Add method 'Extensions1.Add(MyCollection, void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions1.Add(MyCollection, void*)").WithLocation(7, 35), + // (7,35): error CS1503: Argument 2: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "void*").WithLocation(7, 35), + // (7,38): error CS1503: Argument 2: cannot convert from 'int' to 'void*' + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "void*").WithLocation(7, 38), + // (7,40): error CS1950: The best overloaded Add method 'Extensions1.Add(MyCollection, void*)' for the collection initializer has some invalid arguments + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions1.Add(MyCollection, void*)").WithLocation(7, 40)); string sourceB2 = """ + using N; class Program { static void Main() { - MyCollection? x = /**/[[]]/**/; - x.Value.Report(); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); + } + } + namespace N + { + static class Extensions2 + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } } } """; - - comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: "[], "); - - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (1 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[[]]') - Elements(1): - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection?[], IsImplicit) (Syntax: '[]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection?[]) (Syntax: '[]') - Elements(0) - """); + CompileAndVerify([sourceA, sourceB2, s_collectionExtensions], options: TestOptions.UnsafeReleaseExe, expectedOutput: "[1, 2, 3], "); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_08() + public void AddMethod_RefStruct_01() { - string sourceA = """ + string source = """ using System; using System.Collections; using System.Collections.Generic; - class MyCollection : IEnumerable + ref struct R { } + class MyCollection : IEnumerable { - private List _list = new(); - public void Add(params object[] x) + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(R r) => throw null; + internal void __AddInternal(T t) { _list.Add(t); } + } + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } + class Program + { + static void Main() { - Console.Write("Add: "); - foreach (var i in x) - Console.Write("{0}, ", i); - Console.WriteLine(); - _list.AddRange(x); + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; + z.Report(); } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } """; + CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], "); + } - string sourceB1 = """ + [Fact] + public void AddMethod_RefStruct_02() + { + string source = """ + using System; + using System.Collections; + using System.Collections.Generic; + ref struct R + { + public R(object value) { Value = value; } + public readonly object Value; + } + class MyCollection : IEnumerable + { + private List _list = new(); + public MyEnumerator GetEnumerator() => new MyEnumerator(_list); + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(object o) => throw null; + internal void __AddInternal(R r) { _list.Add(r.Value); } + } + class MyEnumerator + { + private List _list; + private int _index = -1; + public MyEnumerator(List list) { _list = list; } + public bool MoveNext() + { + if (_index < _list.Count) _index++; + return _index < _list.Count; + } + public R Current => new R(_list[_index]); + } + static class Extensions + { + public static void Add(this MyCollection collection, R r) { collection.__AddInternal(r); } + } class Program { static void Main() { - object x = 1; - object[] y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; - z.Report(); + MyCollection x = [new R(1)]; + MyCollection y = [..x, new R(2)]; + foreach (var i in y) + Console.Write("{0}, ", i.Value); } } """; + CompileAndVerify(source, verify: Verification.FailsILVerify, expectedOutput: "1, 2, "); + } - var comp = CreateCompilation([sourceB1, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: 1, - Add: 2, - Add: 3, - [1, 2, 3], - """); + [Fact] + public void AddMethod_UseSiteErrors() + { + string assemblyA = GetUniqueName(); + string sourceA = """ + public class A { } + """; + var comp = CreateCompilation(sourceA, assemblyName: assemblyA); + var refA = comp.EmitToImageReference(); - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') - ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + string sourceB = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(A a) => throw null; + public void __AddInternal(T t) { _list.Add(t); } + } + """; + comp = CreateCompilation(sourceB, references: [refA]); + var refB = comp.EmitToImageReference(); - string sourceB2 = """ + string sourceC = """ + static class Extensions + { + public static void Add(this MyCollection collection, T t) { collection.__AddInternal(t); } + } class Program { static void Main() { - object[] x = [1]; - object[][] y = [[2, 3]]; - MyCollection z = /**/[x, ..y]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollection z = [x, ..y]; z.Report(); } } """; - comp = CreateCompilation([sourceB2, sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); - CompileAndVerify(comp, expectedOutput: """ - Add: 1, - Add: 2, 3, - [1, 2, 3], - """); + CompileAndVerify([sourceC, s_collectionExtensions], references: [refA, refB], expectedOutput: "[1, 2, 3], "); - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'x') - ISpreadOperation (ElementType: System.Object[]) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[][]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refB]); + comp.VerifyEmitDiagnostics( + // (11,35): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly '2537f385-b53e-4fea-834a-b23059cd7f17, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "x").WithArguments("A", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(11, 35), + // (11,40): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly '2537f385-b53e-4fea-834a-b23059cd7f17, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // MyCollection z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "y").WithArguments("A", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(11, 40)); } - [WorkItem("https://github.com/dotnet/roslyn/issues/72461")] [Fact] - public void Add_ParamsArray_09() + public void AddMethod_UseSiteErrors_ParamCollection() { - string sourceA1 = """ - public abstract class MyCollectionBase + string assemblyA = GetUniqueName(); + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + public class MyCollectionA : IEnumerable { - public abstract void Add(object[] x); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { _list.Add(t); } } """; - string assemblyName = GetUniqueName(); - var comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(1, 0, 0, 0)), sourceA1, references: TargetFrameworkUtil.StandardReferences); - var refA1 = comp.EmitToImageReference(); + var comp = CreateCompilation(sourceA, assemblyName: assemblyA); + var refA = comp.EmitToImageReference(); string sourceB = """ using System.Collections; using System.Collections.Generic; - public class MyCollection : MyCollectionBase, IEnumerable + public class MyCollectionB : IEnumerable { - private List _list = new(); - public override void Add(object[] x) => _list.AddRange(x); - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(U x, params MyCollectionA y) where U : T { _list.Add(x); _list.AddRange(y); } } """; - comp = CreateCompilation(sourceB, references: [refA1]); + comp = CreateCompilation(sourceB, references: [refA]); var refB = comp.EmitToImageReference(); - string sourceA2 = """ - public abstract class MyCollectionBase - { - public abstract void Add(params object[] x); - } - """; - comp = CreateCompilation(new AssemblyIdentity(assemblyName, new Version(2, 0, 0, 0)), sourceA2, references: TargetFrameworkUtil.StandardReferences); - var refA2 = comp.EmitToImageReference(); - string sourceC = """ class Program { static void Main() { - object x = 1; - object[] y = [2, 3]; - MyCollection z = /**/[x, ..y]/**/; + int x = 1; + int[] y = [2, 3]; + MyCollectionB z = [x, ..y]; z.Report(); } } """; - comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refA2, refB], options: TestOptions.ReleaseExe); - comp.VerifyEmitDiagnostics(); + CompileAndVerify([sourceC, s_collectionExtensions], references: [refA, refB], expectedOutput: "[1, 2, 3], "); - VerifyOperationTreeForTest(comp, - """ - ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y]') - Elements(2): - ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Object) (Syntax: 'x') - ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..y') - Operand: - ILocalReferenceOperation: y (OperationKind.LocalReference, Type: System.Object[]) (Syntax: 'y') - ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (Identity) - """); + comp = CreateCompilation([sourceC, s_collectionExtensions], references: [refB]); + comp.VerifyEmitDiagnostics( + // (7,35): error CS0012: The type 'MyCollectionA<>' is defined in an assembly that is not referenced. You must add a reference to assembly '41f5b758-1e64-4c10-88d8-6dd8029c374c, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // MyCollectionB z = [x, ..y]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "[x, ..y]").WithArguments("MyCollectionA<>", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(7, 35)); } [Fact] @@ -34492,7 +38165,6 @@ void verify(string memberName, string sourceName = null) [Theory] [InlineData((int)WellKnownMember.System_IndexOutOfRangeException__ctor)] - [InlineData((int)WellKnownMember.System_Array__SetValue)] [InlineData((int)WellKnownMember.System_Collections_Generic_EqualityComparer_T__get_Default)] [InlineData((int)WellKnownMember.System_Collections_Generic_EqualityComparer_T__Equals)] public void SynthesizedReadOnlyList_SingleElement_MissingMembers(int missingMember) @@ -34529,6 +38201,7 @@ public void SynthesizedReadOnlyList_SingleElement_MissingTypes(int missingType) [InlineData((int)SpecialMember.System_Collections_IEnumerator__Current)] [InlineData((int)SpecialMember.System_Collections_IEnumerator__MoveNext)] [InlineData((int)SpecialMember.System_Collections_IEnumerator__Reset)] + [InlineData((int)SpecialMember.System_Array__SetValue)] public void SynthesizedReadOnlyList_SingleElement_MissingSpecialMembers(int missingMember) { string source = """ diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index 36a07cc8f3bd8..83bd9f2da8660 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -5723,6 +5723,52 @@ static void Test(int y, params long[] x) ); } + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/72696")] + public void BetterOverload_04_Ambiguity() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} + +static class ExtensionsA +{ + public static void Add(this MyCollection collection, params string[] args) { } +} + +static class ExtensionsB +{ + public static void Add(this MyCollection collection, params string[] args) { } +} + +class Program +{ + static void Main() + { + var x = new MyCollection(); + x.Add(""); + + var y = new MyCollection { "" }; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics( + // (25,11): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add(MyCollection, params string[])' and 'ExtensionsB.Add(MyCollection, params string[])' + // x.Add(""); + Diagnostic(ErrorCode.ERR_AmbigCall, "Add").WithArguments("ExtensionsA.Add(MyCollection, params string[])", "ExtensionsB.Add(MyCollection, params string[])").WithLocation(25, 11), + // (27,44): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add(MyCollection, params string[])' and 'ExtensionsB.Add(MyCollection, params string[])' + // var y = new MyCollection { "" }; + Diagnostic(ErrorCode.ERR_AmbigCall, @"""""").WithArguments("ExtensionsA.Add(MyCollection, params string[])", "ExtensionsB.Add(MyCollection, params string[])").WithLocation(27, 44) + ); + } + [Fact] public void GenericInference() { @@ -14880,5 +14926,164 @@ static void M2(params IEnumerable x) Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "C1").WithLocation(16, 17) ); } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_01() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Element { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + Test(new Element(), null); + } + + static void Test(params ElementCollection c) + { + c.Report(); + } + } + """; + CompileAndVerify([source, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/72098")] + [Fact] + public void AddMethod_Derived_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + + class Base { } + class Element : Base { } + + class ElementCollection : IEnumerable + { + private readonly List _list = new(); + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public void Add(Element element) { _list.Add(element); } + } + + class Program + { + static void Main() + { + Test(new Element(), null); + } + + static void Test(params ElementCollection c) + { + c.Report(); + } + } + """; + CompileAndVerify([source, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[Element, null], "); + } + + [WorkItem("https://github.com/dotnet/roslyn/issues/71240")] + [Fact] + public void AddMethod_Derived_03() + { + string sourceA = """ + using System.Collections; + using System.Collections.Generic; + + class Sample : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { if (t is object[] o) _list.Add(o); } + } + """; + + string sourceB1 = """ + class Program + { + static void Main() + { + Test(["a"], ["b"], ["c"]); + } + + static void Test(params Sample s) + { + s.Report(); + } + } + """; + CompileAndVerify([sourceA, sourceB1, CollectionExpressionTests.s_collectionExtensions], expectedOutput: "[[a], [b], [c]], "); + + string sourceB2 = """ + class Program + { + static void Main() + { + Test("a", null); + } + + static void Test(params Sample s) + { + } + } + """; + var comp = CreateCompilation([sourceA, sourceB2]); + comp.VerifyEmitDiagnostics( + // (5,14): error CS1503: Argument 1: cannot convert from 'string' to 'object[]' + // Test("a", null); + Diagnostic(ErrorCode.ERR_BadArgType, @"""a""").WithArguments("1", "string", "object[]").WithLocation(5, 14) + ); + } + + [Fact] + public void AddMethod_Generic_02() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class MyCollection : IEnumerable + { + private readonly List _list = new(); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + public void Add(T t) { _list.Add(t); } + } + class Program + { + + static void Test(params MyCollection z) + { + } + + static void Main() + { + int x = 1; + Test(x); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (12,22): error CS9215: Collection expression type 'MyCollection' must have an instance or extension method 'Add' that can be called with a single argument. + // static void Test(params MyCollection z) + Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "params MyCollection z").WithArguments("MyCollection").WithLocation(12, 22), + // (19,14): error CS1503: Argument 1: cannot convert from 'int' to 'params MyCollection' + // Test(x); + Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs index 27bd784d69384..fc00e0c57b893 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PatternMatchingTests3.cs @@ -1393,7 +1393,7 @@ static void Main() { } }"; var expectedDiagnostics = new DiagnosticDescription[] { - // (4,19): warning CS8618: Non-nullable field 'o' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,19): warning CS8618: Non-nullable field 'o' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // static object o; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "o").WithArguments("field", "o").WithLocation(4, 19), // (4,19): warning CS0649: Field 'C.o' is never assigned to, and will always have its default value null diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs index bd9fcd94491c7..6cdc9572dc7d3 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs @@ -7117,7 +7117,10 @@ struct Example() Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), + // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope + // public ReadOnlySpan Property { get; } = stackalloc int[512]; + Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); } public static IEnumerable ParameterScope_MemberData() @@ -19668,7 +19671,7 @@ internal class MyOtherClass // (57,32): warning CS0067: The event 'MyOtherClass.SomethingChanged' is never used // public event EventHandler? SomethingChanged; Diagnostic(ErrorCode.WRN_UnreferencedEvent, "SomethingChanged").WithArguments("MyOtherClass.SomethingChanged").WithLocation(57, 32), - // (58,19): warning CS8618: Non-nullable property 'MyProperty' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (58,19): warning CS8618: Non-nullable property 'MyProperty' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public string MyProperty { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "MyProperty").WithArguments("property", "MyProperty").WithLocation(58, 19) ); diff --git a/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs b/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs index ccc34c3e368d9..64cc4a4f131e7 100644 --- a/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs +++ b/src/Compilers/CSharp/Test/EndToEnd/EndToEndTests.cs @@ -172,7 +172,7 @@ void M2() { // This test is a canary attempting to make sure that we don't regress the # of fluent calls that // the compiler can handle. - [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1874763")] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/72678"), WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1874763")] public void OverflowOnFluentCall_ExtensionMethods() { int numberFluentCalls = (IntPtr.Size, ExecutionConditionUtil.Configuration, RuntimeUtilities.IsDesktopRuntime, RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) switch diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableContextTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableContextTests.cs index 08698e915b7b4..68074bbd50312 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableContextTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableContextTests.cs @@ -962,7 +962,7 @@ struct S S(object obj) : this() { } }"; verify(source, expectedAnalyzedKeys: new[] { ".ctor" }, - // (6,5): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,5): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // S(object obj) : this() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "F1").WithLocation(6, 5) ); @@ -1083,7 +1083,7 @@ public void AnalyzeMethodsInEnabledContextOnly_05() static object P2 { get; set; } }"; verify(source, expectedAnalyzedKeys: new[] { ".cctor" }, - // (6,19): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (6,19): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // static object P2 { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P2").WithArguments("property", "P2").WithLocation(6, 19)); @@ -1096,7 +1096,7 @@ public void AnalyzeMethodsInEnabledContextOnly_05() static object P2 { get; set; } }"; verify(source, expectedAnalyzedKeys: new[] { ".ctor" }, - // (4,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (4,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // object P1 { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P1").WithArguments("property", "P1").WithLocation(4, 12)); @@ -1109,7 +1109,7 @@ public void AnalyzeMethodsInEnabledContextOnly_05() object P3 => 3; }"; verify(source, expectedAnalyzedKeys: new[] { ".ctor", "get_P2", "get_P3", "set_P2" }, - // (4,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (4,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // object P1 { get; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P1").WithArguments("property", "P1").WithLocation(4, 12)); @@ -1124,7 +1124,7 @@ class Program static event D E2; }"; verify(source, expectedAnalyzedKeys: new[] { ".cctor" }, - // (8,20): warning CS8618: Non-nullable event 'E2' must contain a non-null value when exiting constructor. Consider declaring the event as nullable. + // (8,20): warning CS8618: Non-nullable event 'E2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable. // static event D E2; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "E2").WithArguments("event", "E2").WithLocation(8, 20)); @@ -1139,7 +1139,7 @@ class Program static event D E2; }"; verify(source, expectedAnalyzedKeys: new[] { ".ctor" }, - // (6,13): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider declaring the event as nullable. + // (6,13): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable. // event D E1; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "E1").WithArguments("event", "E1").WithLocation(6, 13)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index c418ef1be80e0..81d33246c8d68 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -30965,10 +30965,10 @@ public void Init(T t) "; var comp = CreateNullableCompilation(new[] { source, NotNullAttributeDefinition, MemberNotNullAttributeDefinition, DisallowNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(9, 12), - // (10,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C(T t) { Prop = t; } // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(10, 12), // (11,38): warning CS8602: Dereference of a possibly null reference. @@ -31006,10 +31006,10 @@ public void Init(T t) "; var comp = CreateNullableCompilation(new[] { source, NotNullAttributeDefinition, MemberNotNullAttributeDefinition, DisallowNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (9,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C() { } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "field").WithLocation(9, 12), - // (10,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (10,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C(T t) { field = t; } // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "field").WithLocation(10, 12), // (11,39): warning CS8602: Dereference of a possibly null reference. @@ -31078,10 +31078,10 @@ public C3(int i) // We expect no warnings on `C2(string?)` because `C2.F`'s getter and setter are not linked. var comp = CreateNullableCompilation(new[] { source, NotNullAttributeDefinition, DisallowNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable property 'F1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C1() { } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("property", "F1").WithLocation(9, 12), - // (10,12): warning CS8618: Non-nullable property 'F1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C1(string? F1) // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("property", "F1").WithLocation(10, 12), // (12,19): warning CS8601: Possible null reference assignment. @@ -31149,19 +31149,19 @@ public C3(int i) "; var comp = CreateNullableCompilation(new[] { source, NotNullAttributeDefinition, DisallowNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (9,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C1() { } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("field", "F").WithLocation(9, 12), - // (10,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (10,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C1(string? F) // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("field", "F").WithLocation(10, 12), // (12,18): warning CS8601: Possible null reference assignment. // this.F = F; // 3 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "F").WithLocation(12, 18), - // (25,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (25,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C2() { } // 4 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C2").WithArguments("field", "F").WithLocation(25, 12), - // (26,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (26,12): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C2(string? F) // 5 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C2").WithArguments("field", "F").WithLocation(26, 12), // (44,18): warning CS8601: Possible null reference assignment. @@ -31245,10 +31245,10 @@ public void M1(T t) { } // it seems like property state should be NotNull after assigning a MaybeNull to the property. // https://github.com/dotnet/roslyn/issues/49964 comp.VerifyDiagnostics( - // (8,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (8,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(8, 12), - // (12,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (12,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C(T t) // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(12, 12), // (19,9): warning CS8602: Dereference of a possibly null reference. @@ -33905,7 +33905,7 @@ static void X2(ref T location, T value) where T : class? { } "; var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }); comp.VerifyEmitDiagnostics( - // (14,5): warning CS8618: Non-nullable field 'f2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (14,5): warning CS8618: Non-nullable field 'f2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C", isSuppressed: false).WithArguments("field", "f2").WithLocation(14, 5), // (18,16): warning CS8601: Possible null reference assignment. @@ -45895,13 +45895,13 @@ public class CStruct where TStruct : struct }"; var comp = CreateNullableCompilation(new[] { lib_cs, NotNullAttributeDefinition }); comp.VerifyDiagnostics( - // (5,5): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (5,5): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // COpen() // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "COpen").WithArguments("property", "P1").WithLocation(5, 5), // (7,14): warning CS8601: Possible null reference assignment. // P1 = default; // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(7, 14), - // (14,5): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (14,5): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // CClass() // 3 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "CClass").WithArguments("property", "P2").WithLocation(14, 5), // (16,14): warning CS8625: Cannot convert null literal to non-nullable reference type. @@ -60808,7 +60808,7 @@ struct S1 // (12,14): warning CS8601: Possible null reference assignment. // P1 = x1; // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x1").WithLocation(12, 14), - // (10,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C1(C1? x1) // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("property", "P1").WithLocation(10, 12), // (22,14): warning CS8600: Converting null literal or possible null value to non-nullable type. @@ -60895,7 +60895,7 @@ struct S1 // (12,14): warning CS8601: Possible null reference assignment. // P1 = x1; // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x1").WithLocation(12, 14), - // (10,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C1(C0? x1) // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("property", "P1").WithLocation(10, 12), // (34,14): warning CS8600: Converting null literal or possible null value to non-nullable type. @@ -73723,7 +73723,7 @@ static void F4(A? a) { } var comp = CreateCompilation(new[] { source }, options: WithNullableEnable()); // https://github.com/dotnet/roslyn/issues/31018: Report warnings for // 3 and // 4. comp.VerifyDiagnostics( - // (6,30): warning CS8618: Non-nullable event 'E' must contain a non-null value when exiting constructor. Consider declaring the event as nullable. + // (6,30): warning CS8618: Non-nullable event 'E' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable. // event Action> E; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "E").WithArguments("event", "E").WithLocation(6, 30), // (10,17): warning CS8622: Nullability of reference types in type of parameter 'a' of 'void B.F1(A a)' doesn't match the target delegate 'Action>' (possibly because of nullability attributes). @@ -121363,7 +121363,7 @@ static void Test(string? s) }"; CreateCompilation(source, options: WithNullableEnable()).VerifyDiagnostics( - // (5,12): warning CS8618: Non-nullable field 'FieldWithInferredAnnotation' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (5,12): warning CS8618: Non-nullable field 'FieldWithInferredAnnotation' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public T FieldWithInferredAnnotation; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "FieldWithInferredAnnotation").WithArguments("field", "FieldWithInferredAnnotation").WithLocation(5, 12), // (19,5): warning CS8602: Dereference of a possibly null reference. @@ -130261,7 +130261,7 @@ public C() // 1 var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (6,12): warning CS8618: Non-nullable field 'x' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,12): warning CS8618: Non-nullable field 'x' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C() // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "x").WithLocation(6, 12), // (14,19): warning CS8625: Cannot convert null literal to non-nullable reference type. @@ -130295,7 +130295,7 @@ public C() // 1 var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (7,12): warning CS8618: Non-nullable field 'x' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (7,12): warning CS8618: Non-nullable field 'x' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C() // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "x").WithLocation(7, 12), // (15,19): warning CS8625: Cannot convert null literal to non-nullable reference type. @@ -153546,27 +153546,18 @@ public override void M1(C x) class C {} "); comp.VerifyDiagnostics( - // (11,32): error CS0305: Using the generic type 'C' requires 1 type arguments + // (11,26): error CS0115: 'B.M1(C)': no suitable method found to override // public override void M1(C x) - Diagnostic(ErrorCode.ERR_BadArity, "C x").WithArguments("C", "type", "1").WithLocation(11, 32), - // (11,54): error CS1003: Syntax error, ',' expected + Diagnostic(ErrorCode.ERR_OverrideNotExpected, "M1").WithArguments("B.M1(C)").WithLocation(11, 26), + // (11,54): error CS1003: Syntax error, '>' expected // public override void M1(C x) - Diagnostic(ErrorCode.ERR_SyntaxError, "x").WithArguments(",").WithLocation(11, 54), - // (11,54): error CS0246: The type or namespace name 'x' could not be found (are you missing a using directive or an assembly reference?) + Diagnostic(ErrorCode.ERR_SyntaxError, "x").WithArguments(">").WithLocation(11, 54), + // (11,54): error CS0453: The type 'T?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable' // public override void M1(C x) - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "x").WithArguments("x").WithLocation(11, 54), - // (11,55): error CS1003: Syntax error, '>' expected + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "x").WithArguments("System.Nullable", "T", "T?").WithLocation(11, 54), + // (11,54): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable' // public override void M1(C x) - Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments(">").WithLocation(11, 55), - // (11,55): error CS1001: Identifier expected - // public override void M1(C x) - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(11, 55), - // (11,55): error CS0453: The type 'T?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable' - // public override void M1(C x) - Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "").WithArguments("System.Nullable", "T", "T?").WithLocation(11, 55), - // (11,55): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable' - // public override void M1(C x) - Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "").WithArguments("System.Nullable", "T", "T").WithLocation(11, 55) + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "x").WithArguments("System.Nullable", "T", "T").WithLocation(11, 54) ); } @@ -154685,10 +154676,10 @@ public class Nested // (5,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // c.Property.Property2 = null; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 24), - // (13,19): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (13,19): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Nested Property { get; set; } // implicitly means C.Nested Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Property").WithArguments("property", "Property").WithLocation(13, 19), - // (16,18): warning CS8618: Non-nullable property 'Property2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,18): warning CS8618: Non-nullable property 'Property2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public U Property2 { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Property2").WithArguments("property", "Property2").WithLocation(16, 18) ); @@ -154721,10 +154712,10 @@ public class Nested // (5,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // c.Property.Property2 = null; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 24), - // (13,24): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (13,24): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C.Nested Property { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Property").WithArguments("property", "Property").WithLocation(13, 24), - // (16,18): warning CS8618: Non-nullable property 'Property2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,18): warning CS8618: Non-nullable property 'Property2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public U Property2 { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Property2").WithArguments("property", "Property2").WithLocation(16, 18) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 9167634ab8a04..6da08b9784005 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -760,6 +760,12 @@ ref struct S1 // (22,18): error CS8352: Cannot use variable 'local' in this context because it may expose referenced variables outside of their declaration scope // global = local && global; Diagnostic(ErrorCode.ERR_EscapeVariable, "local").WithArguments("local").WithLocation(22, 18), + // (22,18): error CS8347: Cannot use a result of 'S1.operator &(S1, S1)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // global = local && global; + Diagnostic(ErrorCode.ERR_EscapeCall, "local && global").WithArguments("S1.operator &(S1, S1)", "x").WithLocation(22, 18), + // (25,16): error CS8347: Cannot use a result of 'S1.operator |(S1, S1)' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope + // return global || local; + Diagnostic(ErrorCode.ERR_EscapeCall, "global || local").WithArguments("S1.operator |(S1, S1)", "y").WithLocation(25, 16), // (25,26): error CS8352: Cannot use variable 'local' in this context because it may expose referenced variables outside of their declaration scope // return global || local; Diagnostic(ErrorCode.ERR_EscapeVariable, "local").WithArguments("local").WithLocation(25, 26) @@ -7196,5 +7202,1334 @@ static void M(ReadOnlySpan s) var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefStruct_Explicit() + { + var source = """ + class C + { + S M1() + { + S s; + s = (S)100; // 1 + return s; + } + + S M2() + { + return (S)200; // 2 + } + + S M3(in int x) + { + S s; + s = (S)x; // 3 + return s; + } + + S M4(in int x) + { + return (S)x; + } + + S M4s(scoped in int x) + { + return (S)x; // 4 + } + + S M5(in int x) + { + S s = (S)x; + return s; + } + + S M5s(scoped in int x) + { + S s = (S)x; + return s; // 5 + } + + S M6() + { + S s = (S)300; + return s; // 6 + } + + void M7(in int x) + { + scoped S s; + s = (S)x; + s = (S)100; + } + } + + ref struct S + { + public static explicit operator S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,13): error CS8347: Cannot use a result of 'S.explicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = (S)100; // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "(S)100").WithArguments("S.explicit operator S(in int)", "x").WithLocation(6, 13), + // (6,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // s = (S)100; // 1 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "100").WithLocation(6, 16), + // (12,16): error CS8347: Cannot use a result of 'S.explicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return (S)200; // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "(S)200").WithArguments("S.explicit operator S(in int)", "x").WithLocation(12, 16), + // (12,19): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return (S)200; // 2 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "200").WithLocation(12, 19), + // (18,13): error CS8347: Cannot use a result of 'S.explicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = (S)x; // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "(S)x").WithArguments("S.explicit operator S(in int)", "x").WithLocation(18, 13), + // (18,16): error CS9077: Cannot return a parameter by reference 'x' through a ref parameter; it can only be returned in a return statement + // s = (S)x; // 3 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(18, 16), + // (29,16): error CS8347: Cannot use a result of 'S.explicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return (S)x; // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "(S)x").WithArguments("S.explicit operator S(in int)", "x").WithLocation(29, 16), + // (29,19): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method + // return (S)x; // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(29, 19), + // (41,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 5 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(41, 16), + // (47,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 6 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(47, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefStruct_Implicit() + { + var source = """ + class C + { + S M1() + { + S s; + s = 100; // 1 + return s; + } + + S M2() + { + return 200; // 2 + } + + S M3(in int x) + { + S s; + s = x; // 3 + return s; + } + + S M4(in int x) + { + return x; + } + + S M4s(scoped in int x) + { + return x; // 4 + } + + S M5(in int x) + { + S s = x; + return s; + } + + S M5s(scoped in int x) + { + S s = x; + return s; // 5 + } + + S M6() + { + S s = 300; + return s; // 6 + } + + void M7(in int x) + { + scoped S s; + s = x; + s = 100; + } + } + + ref struct S + { + public static implicit operator S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,13): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // s = 100; // 1 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "100").WithLocation(6, 13), + // (6,13): error CS8347: Cannot use a result of 'S.implicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = 100; // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "100").WithArguments("S.implicit operator S(in int)", "x").WithLocation(6, 13), + // (12,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return 200; // 2 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "200").WithLocation(12, 16), + // (12,16): error CS8347: Cannot use a result of 'S.implicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return 200; // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "200").WithArguments("S.implicit operator S(in int)", "x").WithLocation(12, 16), + // (18,13): error CS9077: Cannot return a parameter by reference 'x' through a ref parameter; it can only be returned in a return statement + // s = x; // 3 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(18, 13), + // (18,13): error CS8347: Cannot use a result of 'S.implicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = x; // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "x").WithArguments("S.implicit operator S(in int)", "x").WithLocation(18, 13), + // (29,16): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method + // return x; // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(29, 16), + // (29,16): error CS8347: Cannot use a result of 'S.implicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return x; // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "x").WithArguments("S.implicit operator S(in int)", "x").WithLocation(29, 16), + // (41,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 5 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(41, 16), + // (47,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 6 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(47, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefStructArgument() + { + var source = """ + class C + { + S2 M1() + { + int x = 1; + S1 s1 = (S1)x; + return (S2)s1; // 1 + } + } + + ref struct S1 + { + public static implicit operator S1(in int x) => throw null; + } + + ref struct S2 + { + public static implicit operator S2(S1 s1) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (7,16): error CS8347: Cannot use a result of 'S2.implicit operator S2(S1)' in this context because it may expose variables referenced by parameter 's1' outside of their declaration scope + // return (S2)s1; // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "(S2)s1").WithArguments("S2.implicit operator S2(S1)", "s1").WithLocation(7, 16), + // (7,20): error CS8352: Cannot use variable 's1' in this context because it may expose referenced variables outside of their declaration scope + // return (S2)s1; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s1").WithArguments("s1").WithLocation(7, 20)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefStructArgument_ScopedIn() + { + var source = """ + class C + { + S2 M1() + { + int x = 1; + S1 s1 = (S1)x; + return (S2)s1; + } + } + + ref struct S1 + { + public static implicit operator S1(scoped in int x) => throw null; + } + + ref struct S2 + { + public static implicit operator S2(S1 s1) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RegularStruct() + { + var source = """ + S s; + s = (S)100; + + struct S + { + public static explicit operator S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_StandardImplicitConversion_Input() + { + var source = """ + class C + { + S M1() + { + S s; + s = 100; // 1 + return s; + } + + S M2() + { + return 200; // 2 + } + + S M3(in int x) + { + S s; + s = x; // 3 + return s; + } + + S M4(in int x) + { + return x; // 4 + } + + S M4s(scoped in int x) + { + return x; // 5 + } + + S M5(in int x) + { + S s = x; + return s; // 6 + } + + S M5s(scoped in int x) + { + S s = x; + return s; // 7 + } + + S M6() + { + S s = 300; + return s; // 8 + } + } + + ref struct S + { + public static implicit operator S(in int? x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,13): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // s = 100; // 1 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "100").WithLocation(6, 13), + // (6,13): error CS8347: Cannot use a result of 'S.implicit operator S(in int?)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = 100; // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "100").WithArguments("S.implicit operator S(in int?)", "x").WithLocation(6, 13), + // (12,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return 200; // 2 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "200").WithLocation(12, 16), + // (12,16): error CS8347: Cannot use a result of 'S.implicit operator S(in int?)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return 200; // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "200").WithArguments("S.implicit operator S(in int?)", "x").WithLocation(12, 16), + // (18,13): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // s = x; // 3 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(18, 13), + // (18,13): error CS8347: Cannot use a result of 'S.implicit operator S(in int?)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = x; // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "x").WithArguments("S.implicit operator S(in int?)", "x").WithLocation(18, 13), + // (24,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return x; // 4 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(24, 16), + // (24,16): error CS8347: Cannot use a result of 'S.implicit operator S(in int?)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return x; // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "x").WithArguments("S.implicit operator S(in int?)", "x").WithLocation(24, 16), + // (29,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return x; // 5 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(29, 16), + // (29,16): error CS8347: Cannot use a result of 'S.implicit operator S(in int?)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return x; // 5 + Diagnostic(ErrorCode.ERR_EscapeCall, "x").WithArguments("S.implicit operator S(in int?)", "x").WithLocation(29, 16), + // (35,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 6 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(35, 16), + // (41,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 7 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(41, 16), + // (47,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 8 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(47, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_StandardImplicitConversion_Output() + { + var source = """ + object o; + o = (S)100; + + struct S + { + public static explicit operator S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_StandardImplicitConversion_Both() + { + var source = """ + object o; + int x = 100; + o = (S)x; + + struct S + { + public static explicit operator S(in int? x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_Call() + { + var source = """ + class C + { + S M1(int x) + { + return M2(x); + } + + S M2(S s) => s; + } + + ref struct S + { + public static implicit operator S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,16): error CS8347: Cannot use a result of 'C.M2(S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // return M2(x); + Diagnostic(ErrorCode.ERR_EscapeCall, "M2(x)").WithArguments("C.M2(S)", "s").WithLocation(5, 16), + // (5,19): error CS8166: Cannot return a parameter by reference 'x' because it is not a ref parameter + // return M2(x); + Diagnostic(ErrorCode.ERR_RefReturnParameter, "x").WithArguments("x").WithLocation(5, 19), + // (5,19): error CS8347: Cannot use a result of 'S.implicit operator S(in int)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return M2(x); + Diagnostic(ErrorCode.ERR_EscapeCall, "x").WithArguments("S.implicit operator S(in int)", "x").WithLocation(5, 19)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefReturn_01() + { + var source = """ + class C + { + static ref readonly int M1(int x) + { + return ref M2(x); + } + + static ref readonly int M2(in S s) => throw null; + } + + struct S + { + public static implicit operator S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,20): error CS8347: Cannot use a result of 'C.M2(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // return ref M2(x); + Diagnostic(ErrorCode.ERR_EscapeCall, "M2(x)").WithArguments("C.M2(in S)", "s").WithLocation(5, 20), + // (5,23): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return ref M2(x); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(5, 23)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefReturn_02() + { + var source = """ + class C + { + static ref readonly int M1(int x) + { + return ref M2(x); + } + + static ref readonly int M2(in S s) => throw null; + } + + struct S + { + public static implicit operator ref S(in int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,20): error CS8347: Cannot use a result of 'C.M2(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // return ref M2(x); + Diagnostic(ErrorCode.ERR_EscapeCall, "M2(x)").WithArguments("C.M2(in S)", "s").WithLocation(5, 20), + // (5,23): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return ref M2(x); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(5, 23), + // (13,37): error CS1073: Unexpected token 'ref' + // public static implicit operator ref S(in int x) => throw null; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(13, 37)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedCast_RefReturn_IL() + { + // public struct S + // { + // public static implicit operator ref readonly S(in int x) => throw null; + // } + var ilSource = """ + .class public sequential ansi sealed beforefieldinit S extends System.ValueType + { + .pack 0 + .size 1 + + .method public hidebysig specialname static valuetype S& modreq(System.Runtime.InteropServices.InAttribute) op_Implicit ( + [in] int32& x + ) cil managed + { + .param [1] + .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + + .maxstack 1 + .locals init ( + [0] valuetype S + ) + + ldnull + throw + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.InteropServices.InAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + + .class public auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute extends System.Object + { + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed + { + .maxstack 8 + ret + } + } + """; + + var source = """ + class C + { + static ref readonly int M1(int x) + { + return ref M2(x); + } + + static ref readonly int M2(in S s) => throw null; + } + """; + CreateCompilationWithIL(source, ilSource).VerifyDiagnostics( + // (5,20): error CS8347: Cannot use a result of 'C.M2(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // return ref M2(x); + Diagnostic(ErrorCode.ERR_EscapeCall, "M2(x)").WithArguments("C.M2(in S)", "s").WithLocation(5, 20), + // (5,23): error CS0570: 'S.implicit operator S(in int)' is not supported by the language + // return ref M2(x); + Diagnostic(ErrorCode.ERR_BindToBogus, "x").WithArguments("S.implicit operator S(in int)").WithLocation(5, 23), + // (5,23): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return ref M2(x); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "x").WithLocation(5, 23)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct() + { + var source = """ + class C + { + S M1() + { + S s; + s = 100 + default(S); // 1 + return s; + } + + S M2() + { + return 200 + default(S); // 2 + } + + S M3(in int x) + { + S s; + s = x + default(S); // 3 + return s; + } + + S M4(in int x) + { + return x + default(S); + } + + S M4s(scoped in int x) + { + return x + default(S); // 4 + } + + S M5(in int x) + { + S s = x + default(S); + return s; + } + + S M5s(scoped in int x) + { + S s = x + default(S); + return s; // 5 + } + + S M6() + { + S s = 300 + default(S); + return s; // 6 + } + + void M7(in int x) + { + scoped S s; + s = x + default(S); + s = 100 + default(S); + } + } + + ref struct S + { + public static S operator+(in int x, S y) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,13): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // s = 100 + default(S); // 1 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "100").WithLocation(6, 13), + // (6,13): error CS8347: Cannot use a result of 'S.operator +(in int, S)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = 100 + default(S); // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "100 + default(S)").WithArguments("S.operator +(in int, S)", "x").WithLocation(6, 13), + // (12,16): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return 200 + default(S); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "200").WithLocation(12, 16), + // (12,16): error CS8347: Cannot use a result of 'S.operator +(in int, S)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return 200 + default(S); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "200 + default(S)").WithArguments("S.operator +(in int, S)", "x").WithLocation(12, 16), + // (18,13): error CS9077: Cannot return a parameter by reference 'x' through a ref parameter; it can only be returned in a return statement + // s = x + default(S); // 3 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(18, 13), + // (18,13): error CS8347: Cannot use a result of 'S.operator +(in int, S)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // s = x + default(S); // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "x + default(S)").WithArguments("S.operator +(in int, S)", "x").WithLocation(18, 13), + // (29,16): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method + // return x + default(S); // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(29, 16), + // (29,16): error CS8347: Cannot use a result of 'S.operator +(in int, S)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return x + default(S); // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "x + default(S)").WithArguments("S.operator +(in int, S)", "x").WithLocation(29, 16), + // (41,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 5 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(41, 16), + // (47,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 6 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(47, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Scoped_Left() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static R operator +(scoped R x, R y) => default; + } + class Program + { + static R F() + { + return new R(1) + new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (11,16): error CS8347: Cannot use a result of 'R.operator +(scoped R, R)' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) + new R(2)").WithArguments("R.operator +(scoped R, R)", "y").WithLocation(11, 16), + // (11,27): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(2)").WithArguments("R.R(in int)", "i").WithLocation(11, 27), + // (11,33): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "2").WithLocation(11, 33)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Scoped_Right() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static R operator +(R x, scoped R y) => default; + } + class Program + { + static R F() + { + return new R(1) + new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (11,16): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1)").WithArguments("R.R(in int)", "i").WithLocation(11, 16), + // (11,16): error CS8347: Cannot use a result of 'R.operator +(R, scoped R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) + new R(2)").WithArguments("R.operator +(R, scoped R)", "x").WithLocation(11, 16), + // (11,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(11, 22)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Scoped_Both() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static R operator +(scoped R x, scoped R y) => default; + } + class Program + { + static R F() + { + return new R(1) + new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Scoped_None() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static R operator +(R x, R y) => default; + } + class Program + { + static R F() + { + return new R(1) + new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (11,16): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1)").WithArguments("R.R(in int)", "i").WithLocation(11, 16), + // (11,16): error CS8347: Cannot use a result of 'R.operator +(R, R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) + new R(2)").WithArguments("R.operator +(R, R)", "x").WithLocation(11, 16), + // (11,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) + new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(11, 22)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound() + { + var source = """ + public ref struct C + { + public static C operator +(C left, C right) => right; + public static C X(C left, C right) => right; + public C M(C c, scoped C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (7,9): error CS8347: Cannot use a result of 'C.operator +(C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope + // c += c1; + Diagnostic(ErrorCode.ERR_EscapeCall, "c += c1").WithArguments("C.operator +(C, C)", "right").WithLocation(7, 9), + // (7,14): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope + // c += c1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(7, 14), + // (8,13): error CS8347: Cannot use a result of 'C.operator +(C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope + // c = c + c1; + Diagnostic(ErrorCode.ERR_EscapeCall, "c + c1").WithArguments("C.operator +(C, C)", "right").WithLocation(8, 13), + // (8,17): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope + // c = c + c1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(8, 17), + // (9,13): error CS8347: Cannot use a result of 'C.X(C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope + // c = X(c, c1); + Diagnostic(ErrorCode.ERR_EscapeCall, "X(c, c1)").WithArguments("C.X(C, C)", "right").WithLocation(9, 13), + // (9,18): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope + // c = X(c, c1); + Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(9, 18)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_Scoped_Left() + { + var source = """ + public ref struct C + { + public static C operator +(scoped C left, C right) => right; + public static C X(scoped C left, C right) => right; + public C M(C c, scoped C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (7,9): error CS8347: Cannot use a result of 'C.operator +(scoped C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope + // c += c1; + Diagnostic(ErrorCode.ERR_EscapeCall, "c += c1").WithArguments("C.operator +(scoped C, C)", "right").WithLocation(7, 9), + // (7,14): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope + // c += c1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(7, 14), + // (8,13): error CS8347: Cannot use a result of 'C.operator +(scoped C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope + // c = c + c1; + Diagnostic(ErrorCode.ERR_EscapeCall, "c + c1").WithArguments("C.operator +(scoped C, C)", "right").WithLocation(8, 13), + // (8,17): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope + // c = c + c1; + Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(8, 17), + // (9,13): error CS8347: Cannot use a result of 'C.X(scoped C, C)' in this context because it may expose variables referenced by parameter 'right' outside of their declaration scope + // c = X(c, c1); + Diagnostic(ErrorCode.ERR_EscapeCall, "X(c, c1)").WithArguments("C.X(scoped C, C)", "right").WithLocation(9, 13), + // (9,18): error CS8352: Cannot use variable 'scoped C c1' in this context because it may expose referenced variables outside of their declaration scope + // c = X(c, c1); + Diagnostic(ErrorCode.ERR_EscapeVariable, "c1").WithArguments("scoped C c1").WithLocation(9, 18)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_Scoped_Right() + { + var source = """ + public ref struct C + { + public static C operator +(C left, scoped C right) => left; + public static C X(C left, scoped C right) => left; + public C M(C c, scoped C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_Scoped_Both() + { + var source = """ + public ref struct C + { + public static C operator +(scoped C left, scoped C right) => right; + public static C X(scoped C left, scoped C right) => right; + public C M(C c, scoped C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (3,66): error CS8352: Cannot use variable 'scoped C right' in this context because it may expose referenced variables outside of their declaration scope + // public static C operator +(scoped C left, scoped C right) => right; + Diagnostic(ErrorCode.ERR_EscapeVariable, "right").WithArguments("scoped C right").WithLocation(3, 66), + // (4,57): error CS8352: Cannot use variable 'scoped C right' in this context because it may expose referenced variables outside of their declaration scope + // public static C X(scoped C left, scoped C right) => right; + Diagnostic(ErrorCode.ERR_EscapeVariable, "right").WithArguments("scoped C right").WithLocation(4, 57)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_ScopedTarget() + { + var source = """ + public ref struct C + { + public static C operator +(C left, C right) => right; + public static C X(C left, C right) => right; + public C M(scoped C c, C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c1; + } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_Scoped_Left_ScopedTarget() + { + var source = """ + public ref struct C + { + public static C operator +(scoped C left, C right) => right; + public static C X(scoped C left, C right) => right; + public C M(scoped C c, C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c1; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_Scoped_Right_ScopedTarget() + { + var source = """ + public ref struct C + { + public static C operator +(C left, scoped C right) => left; + public static C X(C left, scoped C right) => left; + public C M(scoped C c, C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c1; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedBinaryOperator_RefStruct_Compound_Scoped_Both_ScopedTarget() + { + var source = """ + public ref struct C + { + public static C operator +(scoped C left, scoped C right) => throw null; + public static C X(scoped C left, scoped C right) => throw null; + public C M(scoped C c, C c1) + { + c += c1; + c = c + c1; + c = X(c, c1); + return c1; + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedUnaryOperator_RefStruct() + { + var source = """ + class C + { + S M1() + { + S s; + s = +s; // 1 + return s; + } + + S M2() + { + return +new S(); // 2 + } + + S M3(in S x) + { + S s; + s = +x; // 3 + return s; + } + + S M4(in S x) + { + return +x; + } + + S M4s(scoped in S x) + { + return +x; // 4 + } + + S M5(in S x) + { + S s = +x; + return s; + } + + S M5s(scoped in S x) + { + S s = +x; + return s; // 5 + } + + S M6() + { + S s = +new S(); + return s; // 6 + } + + void M7(in S x) + { + scoped S s; + s = +x; + s = +new S(); + } + } + + ref struct S + { + public static S operator+(in S s) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,13): error CS8347: Cannot use a result of 'S.operator +(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // s = +s; // 1 + Diagnostic(ErrorCode.ERR_EscapeCall, "+s").WithArguments("S.operator +(in S)", "s").WithLocation(6, 13), + // (6,14): error CS8168: Cannot return local 's' by reference because it is not a ref local + // s = +s; // 1 + Diagnostic(ErrorCode.ERR_RefReturnLocal, "s").WithArguments("s").WithLocation(6, 14), + // (12,16): error CS8347: Cannot use a result of 'S.operator +(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // return +new S(); // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "+new S()").WithArguments("S.operator +(in S)", "s").WithLocation(12, 16), + // (12,17): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return +new S(); // 2 + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "new S()").WithLocation(12, 17), + // (18,13): error CS8347: Cannot use a result of 'S.operator +(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // s = +x; // 3 + Diagnostic(ErrorCode.ERR_EscapeCall, "+x").WithArguments("S.operator +(in S)", "s").WithLocation(18, 13), + // (18,14): error CS9077: Cannot return a parameter by reference 'x' through a ref parameter; it can only be returned in a return statement + // s = +x; // 3 + Diagnostic(ErrorCode.ERR_RefReturnOnlyParameter, "x").WithArguments("x").WithLocation(18, 14), + // (29,16): error CS8347: Cannot use a result of 'S.operator +(in S)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope + // return +x; // 4 + Diagnostic(ErrorCode.ERR_EscapeCall, "+x").WithArguments("S.operator +(in S)", "s").WithLocation(29, 16), + // (29,17): error CS9075: Cannot return a parameter by reference 'x' because it is scoped to the current method + // return +x; // 4 + Diagnostic(ErrorCode.ERR_RefReturnScopedParameter, "x").WithArguments("x").WithLocation(29, 17), + // (41,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 5 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(41, 16), + // (47,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 6 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(47, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedUnaryOperator_RefStruct_Scoped() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static R operator !(scoped R r) => default; + } + class Program + { + static R F() + { + return !new R(0); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedLogicalOperator_RefStruct() + { + var source = """ + class C + { + S M1(S s1, S s2) + { + S s = s1 && s2; + return s; // 1 + } + + S M2(S s1, S s2) + { + return s1 && s2; // 2 + } + + S M3(in S s1, in S s2) + { + S s = s1 && s2; + return s; + } + + S M4(scoped in S s1, in S s2) + { + S s = s1 && s2; + return s; // 3 + } + + S M5(in S s1, scoped in S s2) + { + S s = s1 && s2; + return s; // 4 + } + } + + ref struct S + { + public static bool operator true(in S s) => throw null; + public static bool operator false(in S s) => throw null; + public static S operator &(in S x, in S y) => throw null; + public static S operator |(in S x, in S y) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (6,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 1 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(6, 16), + // (11,16): error CS8166: Cannot return a parameter by reference 's1' because it is not a ref parameter + // return s1 && s2; // 2 + Diagnostic(ErrorCode.ERR_RefReturnParameter, "s1").WithArguments("s1").WithLocation(11, 16), + // (11,16): error CS8347: Cannot use a result of 'S.operator &(in S, in S)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return s1 && s2; // 2 + Diagnostic(ErrorCode.ERR_EscapeCall, "s1 && s2").WithArguments("S.operator &(in S, in S)", "x").WithLocation(11, 16), + // (23,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 3 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(23, 16), + // (29,16): error CS8352: Cannot use variable 's' in this context because it may expose referenced variables outside of their declaration scope + // return s; // 4 + Diagnostic(ErrorCode.ERR_EscapeVariable, "s").WithArguments("s").WithLocation(29, 16)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedLogicalOperator_RefStruct_Scoped_Left() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static bool operator true(R r) => true; + public static bool operator false(R r) => false; + public static R operator |(scoped R x, R y) => default; + } + class Program + { + static R F() + { + return new R(1) || new R(2); + } + + static R F2() + { + return new R(1) | new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (13,16): error CS8347: Cannot use a result of 'R.operator |(scoped R, R)' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) || new R(2)").WithArguments("R.operator |(scoped R, R)", "y").WithLocation(13, 16), + // (13,28): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(2)").WithArguments("R.R(in int)", "i").WithLocation(13, 28), + // (13,34): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "2").WithLocation(13, 34), + // (18,16): error CS8347: Cannot use a result of 'R.operator |(scoped R, R)' in this context because it may expose variables referenced by parameter 'y' outside of their declaration scope + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) | new R(2)").WithArguments("R.operator |(scoped R, R)", "y").WithLocation(18, 16), + // (18,27): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(2)").WithArguments("R.R(in int)", "i").WithLocation(18, 27), + // (18,33): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "2").WithLocation(18, 33)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedLogicalOperator_RefStruct_Scoped_Right() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static bool operator true(R r) => true; + public static bool operator false(R r) => false; + public static R operator |(R x, scoped R y) => default; + } + class Program + { + static R F() + { + return new R(1) || new R(2); + } + + static R F2() + { + return new R(1) | new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (13,16): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1)").WithArguments("R.R(in int)", "i").WithLocation(13, 16), + // (13,16): error CS8347: Cannot use a result of 'R.operator |(R, scoped R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) || new R(2)").WithArguments("R.operator |(R, scoped R)", "x").WithLocation(13, 16), + // (13,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(13, 22), + // (18,16): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1)").WithArguments("R.R(in int)", "i").WithLocation(18, 16), + // (18,16): error CS8347: Cannot use a result of 'R.operator |(R, scoped R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) | new R(2)").WithArguments("R.operator |(R, scoped R)", "x").WithLocation(18, 16), + // (18,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(18, 22)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedLogicalOperator_RefStruct_Scoped_Both() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static bool operator true(R r) => true; + public static bool operator false(R r) => false; + public static R operator |(scoped R x, scoped R y) => default; + } + class Program + { + static R F() + { + return new R(1) || new R(2); + } + + static R F2() + { + return new R(1) | new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71773")] + public void UserDefinedLogicalOperator_RefStruct_Scoped_None() + { + var source = """ + ref struct R + { + private ref readonly int _i; + public R(in int i) { _i = ref i; } + public static bool operator true(R r) => true; + public static bool operator false(R r) => false; + public static R operator |(R x, R y) => default; + } + class Program + { + static R F() + { + return new R(1) || new R(2); + } + + static R F2() + { + return new R(1) | new R(2); + } + } + """; + CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyDiagnostics( + // (13,16): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1)").WithArguments("R.R(in int)", "i").WithLocation(13, 16), + // (13,16): error CS8347: Cannot use a result of 'R.operator |(R, R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) || new R(2)").WithArguments("R.operator |(R, R)", "x").WithLocation(13, 16), + // (13,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) || new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(13, 22), + // (18,16): error CS8347: Cannot use a result of 'R.R(in int)' in this context because it may expose variables referenced by parameter 'i' outside of their declaration scope + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1)").WithArguments("R.R(in int)", "i").WithLocation(18, 16), + // (18,16): error CS8347: Cannot use a result of 'R.operator |(R, R)' in this context because it may expose variables referenced by parameter 'x' outside of their declaration scope + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_EscapeCall, "new R(1) | new R(2)").WithArguments("R.operator |(R, R)", "x").WithLocation(18, 16), + // (18,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return new R(1) | new R(2); + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "1").WithLocation(18, 22)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs index 6987f658c4acb..36d65147ec905 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs @@ -3275,19 +3275,19 @@ struct S3 }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( - // (10,12): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (10,12): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S1() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("field", "F1").WithLocation(10, 12), // (10,12): error CS0171: Field 'S1.F1' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the field. // public S1() { } Diagnostic(ErrorCode.ERR_UnassignedThisUnsupportedVersion, "S1").WithArguments("S1.F1", "11.0").WithLocation(10, 12), - // (16,5): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (16,5): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // S2(object? obj) { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S2").WithArguments("field", "F2").WithLocation(16, 5), // (16,5): error CS0171: Field 'S2.F2' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the field. // S2(object? obj) { } Diagnostic(ErrorCode.ERR_UnassignedThisUnsupportedVersion, "S2").WithArguments("S2.F2", "11.0").WithLocation(16, 5), - // (21,12): warning CS8618: Non-nullable field 'F3' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (21,12): warning CS8618: Non-nullable field 'F3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S3() { F3 = GetValue(); } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S3").WithArguments("field", "F3").WithLocation(21, 12), // (21,24): warning CS8601: Possible null reference assignment. @@ -3296,13 +3296,13 @@ struct S3 comp = CreateCompilation(source, parseOptions: TestOptions.Regular11); comp.VerifyDiagnostics( - // (10,12): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (10,12): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S1() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("field", "F1").WithLocation(10, 12), - // (16,5): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (16,5): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // S2(object? obj) { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S2").WithArguments("field", "F2").WithLocation(16, 5), - // (21,12): warning CS8618: Non-nullable field 'F3' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (21,12): warning CS8618: Non-nullable field 'F3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S3() { F3 = GetValue(); } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S3").WithArguments("field", "F3").WithLocation(21, 12), // (21,24): warning CS8601: Possible null reference assignment. @@ -3721,9 +3721,15 @@ public Example() {} // (5,38): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Field = stackalloc int[512]; Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 38), + // (5,38): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope + // public ReadOnlySpan Field = stackalloc int[512]; + Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 38), // (6,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(6, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(6, 50), + // (6,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope + // public ReadOnlySpan Property { get; } = stackalloc int[512]; + Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(6, 50)); } [WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")] @@ -3802,7 +3808,10 @@ public Example() {} Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), + // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope + // public ReadOnlySpan Property { get; } = stackalloc int[512]; + Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); } [WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")] @@ -3829,7 +3838,10 @@ record struct Example() Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), + // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope + // public ReadOnlySpan Property { get; } = stackalloc int[512]; + Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); } [WorkItem(60568, "https://github.com/dotnet/roslyn/issues/60568")] @@ -3856,7 +3868,10 @@ class Example Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(5, 12), // (5,50): error CS8353: A result of a stackalloc expression of type 'Span' cannot be used in this context because it may be exposed outside of the containing method // public ReadOnlySpan Property { get; } = stackalloc int[512]; - Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50)); + Diagnostic(ErrorCode.ERR_EscapeStackAlloc, "stackalloc int[512]").WithArguments("System.Span").WithLocation(5, 50), + // (5,50): error CS8347: Cannot use a result of 'Span.implicit operator ReadOnlySpan(Span)' in this context because it may expose variables referenced by parameter 'span' outside of their declaration scope + // public ReadOnlySpan Property { get; } = stackalloc int[512]; + Diagnostic(ErrorCode.ERR_EscapeCall, "stackalloc int[512]").WithArguments("System.Span.implicit operator System.ReadOnlySpan(System.Span)", "span").WithLocation(5, 50)); } [ConditionalFact(typeof(CoreClrOnly))] // For conversion from Span to ReadOnlySpan. @@ -4590,10 +4605,10 @@ public S(bool unused) }"; var verifier = CompileAndVerify(source, options: TestOptions.DebugDll.WithSpecificDiagnosticOptions(ReportStructInitializationWarnings)); verifier.VerifyDiagnostics( - // (21,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (21,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "TField").WithLocation(21, 12), - // (21,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (21,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "AutoProp").WithLocation(21, 12), // (23,20): warning CS8625: Cannot convert null literal to non-nullable reference type. @@ -4602,10 +4617,10 @@ public S(bool unused) // (24,18): warning CS8601: Possible null reference assignment. // TField = default; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(24, 18), - // (29,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (29,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "TField").WithLocation(29, 12), - // (29,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (29,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "AutoProp").WithLocation(29, 12), // (29,12): warning CS9022: Control is returned to caller before field 'S.TField' is explicitly assigned, causing a preceding implicit assignment of 'default'. @@ -4621,10 +4636,10 @@ public S(bool unused) verifier = CompileAndVerify(source, options: TestOptions.DebugDll); verifier.VerifyDiagnostics( - // (21,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (21,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "TField").WithLocation(21, 12), - // (21,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (21,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "AutoProp").WithLocation(21, 12), // (23,20): warning CS8625: Cannot convert null literal to non-nullable reference type. @@ -4633,10 +4648,10 @@ public S(bool unused) // (24,18): warning CS8601: Possible null reference assignment. // TField = default; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(24, 18), - // (29,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (29,12): warning CS8618: Non-nullable field 'TField' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "TField").WithLocation(29, 12), - // (29,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (29,12): warning CS8618: Non-nullable property 'AutoProp' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "AutoProp").WithLocation(29, 12)); verifyIL(); @@ -4698,7 +4713,7 @@ public S(bool unused) }"; var comp = CreateCompilation(new[] { source }, options: WithNullableEnable(), parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( - // (4,12): warning CS8618: Non-nullable field 'Item' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,12): warning CS8618: Non-nullable field 'Item' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "Item").WithLocation(4, 12), // (4,12): error CS0171: Field 'S.Item' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the field. @@ -4708,7 +4723,7 @@ public S(bool unused) var verifier = CompileAndVerify(new[] { source }, options: WithNullableEnable(), parseOptions: TestOptions.Regular11); verifier.VerifyDiagnostics( - // (4,12): warning CS8618: Non-nullable field 'Item' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,12): warning CS8618: Non-nullable field 'Item' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "Item").WithLocation(4, 12)); verifier.VerifyIL("S..ctor", @" @@ -4738,7 +4753,7 @@ public S(bool unused) : this() }"; var verifier = CompileAndVerify(new[] { source }, options: WithNullableEnable(), parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); verifier.VerifyDiagnostics( - // (4,12): warning CS8618: Non-nullable field 'Item' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,12): warning CS8618: Non-nullable field 'Item' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "Item").WithLocation(4, 12) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UninitializedNonNullableFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UninitializedNonNullableFieldTests.cs index e1c10b4bed11d..cdc7c38eca163 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UninitializedNonNullableFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UninitializedNonNullableFieldTests.cs @@ -867,7 +867,7 @@ public S1(string s1, string s2) : this(s1) // (12,9): warning CS8602: Dereference of a possibly null reference. // Prop.ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Prop").WithLocation(12, 9), - // (15,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S1(object obj1, object obj2) : this() // 4 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("property", "Prop").WithLocation(15, 12)); @@ -879,7 +879,7 @@ public S1(string s1, string s2) : this(s1) // (12,9): warning CS8602: Dereference of a possibly null reference. // Prop.ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Prop").WithLocation(12, 9), - // (15,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S1(object obj1, object obj2) : this() // 4 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("property", "Prop").WithLocation(15, 12)); @@ -954,7 +954,7 @@ public S1(string s1, string s2) : this(s1) // (10,9): warning CS8602: Dereference of a possibly null reference. // field.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(10, 9), - // (13,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (13,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S1(object obj1, object obj2) : this() // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("field", "field").WithLocation(13, 12)); } @@ -1051,7 +1051,7 @@ public S1(string s) // 1, 2 "; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics( - // (13,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (13,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S1(string s) // 1, 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("field", "field").WithLocation(13, 12), // (13,12): error CS0171: Field 'S1.field' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the field. @@ -1064,7 +1064,7 @@ public S1(string s) // 1, 2 var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular11); verifier.VerifyDiagnostics( - // (13,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (13,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S1(string s) // 1, 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S1").WithArguments("field", "field").WithLocation(13, 12) ); @@ -1781,10 +1781,10 @@ internal S(string s) }"; var comp = CreateCompilation(new[] { source }, options: WithNullableEnable(), parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( - // (6,14): warning CS8618: Non-nullable property 'P' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (6,14): warning CS8618: Non-nullable property 'P' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // internal S(string s) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "P").WithLocation(6, 14), - // (6,14): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,14): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // internal S(string s) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "F").WithLocation(6, 14), // (6,14): error CS0843: Auto-implemented property 'S.P' must be fully assigned before control is returned to the caller. Consider updating to language version '11.0' to auto-default the property. @@ -1796,10 +1796,10 @@ internal S(string s) var verifier = CompileAndVerify(new[] { source }, options: WithNullableEnable(), parseOptions: TestOptions.Regular11); verifier.VerifyDiagnostics( - // (6,14): warning CS8618: Non-nullable property 'P' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (6,14): warning CS8618: Non-nullable property 'P' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // internal S(string s) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "P").WithLocation(6, 14), - // (6,14): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,14): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // internal S(string s) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "F").WithLocation(6, 14)); verifier.VerifyIL("S..ctor", @" @@ -1984,13 +1984,13 @@ public interface I // (4,19): error CS0525: Interfaces cannot contain instance fields // public object F1; // 1 Diagnostic(ErrorCode.ERR_InterfacesCantContainFields, "F1").WithLocation(4, 19), - // (5,26): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (5,26): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static object F2; // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "F2").WithArguments("field", "F2").WithLocation(5, 26), - // (6,26): warning CS8618: Non-nullable property 'F3' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (6,26): warning CS8618: Non-nullable property 'F3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public static object F3 { get; set; } // 3 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "F3").WithArguments("property", "F3").WithLocation(6, 26), - // (7,39): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider declaring the event as nullable. + // (7,39): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable. // public static event System.Action E1; // 4, 5 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "E1").WithArguments("event", "E1").WithLocation(7, 39), // (7,39): warning CS0067: The event 'I.E1' is never used @@ -2030,13 +2030,13 @@ static I() // 2, 3, 4 // (6,39): warning CS0067: The event 'I.E1' is never used // public static event System.Action E1; // 1 Diagnostic(ErrorCode.WRN_UnreferencedEvent, "E1").WithArguments("I.E1").WithLocation(6, 39), - // (16,12): warning CS8618: Non-nullable property 'F2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,12): warning CS8618: Non-nullable property 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // static I() // 2, 3, 4 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "I").WithArguments("property", "F2").WithLocation(16, 12), - // (16,12): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider declaring the event as nullable. + // (16,12): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable. // static I() // 2, 3, 4 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "I").WithArguments("event", "E1").WithLocation(16, 12), - // (16,12): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (16,12): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // static I() // 2, 3, 4 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "I").WithArguments("field", "F1").WithLocation(16, 12) ); @@ -2077,10 +2077,10 @@ void L(object o) // Null state does not flow out of local functions https://github.com/dotnet/roslyn/issues/45770 var comp = CreateCompilation(new[] { source }, options: WithNullableEnable(), parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( - // (6,5): warning CS8618: Non-nullable field 'G' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,5): warning CS8618: Non-nullable field 'G' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "G").WithLocation(6, 5), - // (6,5): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,5): warning CS8618: Non-nullable field 'F' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "F").WithLocation(6, 5)); } @@ -2188,7 +2188,7 @@ public C3() }"; var comp = CreateCompilation(new[] { source, DoesNotReturnIfAttributeDefinition }, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C1() // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C1").WithArguments("property", "Prop").WithLocation(9, 12)); } @@ -2336,7 +2336,7 @@ public Derived() "; var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (4,19): warning CS8618: Non-nullable property 'BaseProp' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (4,19): warning CS8618: Non-nullable property 'BaseProp' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public string BaseProp { get; set; } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "BaseProp").WithArguments("property", "BaseProp").WithLocation(4, 19), // (14,9): warning CS8602: Dereference of a possibly null reference. @@ -2558,21 +2558,21 @@ public partial class Class1 comp.GetDiagnosticsForSyntaxTree(CompilationStage.Compile, comp.SyntaxTrees[0], filterSpanWithinTree: null, includeEarlierStages: true) .Verify( - // (4,35): warning CS8618: Non-nullable field 'Value1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,35): warning CS8618: Non-nullable field 'Value1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static readonly string Value1; // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Value1").WithArguments("field", "Value1").WithLocation(4, 35)); comp.GetDiagnosticsForSyntaxTree(CompilationStage.Compile, comp.SyntaxTrees[1], filterSpanWithinTree: null, includeEarlierStages: true) .Verify( - // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static readonly string Value2; // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Value2").WithArguments("field", "Value2").WithLocation(4, 35)); comp.VerifyDiagnostics( - // (4,35): warning CS8618: Non-nullable field 'Value1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,35): warning CS8618: Non-nullable field 'Value1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static readonly string Value1; // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Value1").WithArguments("field", "Value1").WithLocation(4, 35), - // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static readonly string Value2; // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Value2").WithArguments("field", "Value2").WithLocation(4, 35)); } @@ -2599,12 +2599,12 @@ public partial class Class1 comp.GetDiagnosticsForSyntaxTree(CompilationStage.Compile, comp.SyntaxTrees[1], filterSpanWithinTree: null, includeEarlierStages: true) .Verify( - // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static readonly string Value2; // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Value2").WithArguments("field", "Value2").WithLocation(4, 35)); comp.VerifyDiagnostics( - // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (4,35): warning CS8618: Non-nullable field 'Value2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public static readonly string Value2; // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Value2").WithArguments("field", "Value2").WithLocation(4, 35)); } @@ -2741,7 +2741,7 @@ public C() { } }"; var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (4,12): warning CS8618: Non-nullable property 'S' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (4,12): warning CS8618: Non-nullable property 'S' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "S").WithLocation(4, 12)); @@ -2762,7 +2762,7 @@ public class C }"; var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (4,19): warning CS8618: Non-nullable property 'S' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (4,19): warning CS8618: Non-nullable property 'S' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public string S { get; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "S").WithLocation(4, 19)); @@ -2798,7 +2798,7 @@ static void Main() var diagnostics = comp.GetDiagnostics(); diagnostics.Verify( - // (8,14): warning CS8618: Non-nullable field 'f' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (8,14): warning CS8618: Non-nullable field 'f' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public B f; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "f").WithArguments("field", "f").WithLocation(8, 14).WithWarningAsError(warnAsError)); @@ -2824,10 +2824,10 @@ public Rec(Rec other) // 2 var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( - // 0.cs(6,19): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // 0.cs(6,19): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public string Prop { get; init; } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Prop").WithArguments("property", "Prop").WithLocation(6, 19), - // 0.cs(7,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // 0.cs(7,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Rec(Rec other) // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Rec").WithArguments("property", "Prop").WithLocation(7, 12)); } @@ -2847,7 +2847,7 @@ public Rec(Rec other) // 1 var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); comp.VerifyEmitDiagnostics( - // 0.cs(4,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // 0.cs(4,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Rec(Rec other) // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Rec").WithArguments("property", "Prop").WithLocation(4, 12)); } @@ -2922,7 +2922,7 @@ public void M() var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition, RequiredMemberAttribute, SetsRequiredMembersAttribute, CompilerFeatureRequiredAttribute }); comp.VerifyEmitDiagnostics( - // 0.cs(9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // 0.cs(9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Rec(Rec other) { } // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Rec").WithArguments("property", "Prop").WithLocation(9, 12)); } diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index e9f6d34062d2d..6ad935d3702cc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -5943,7 +5943,7 @@ public void GetSpecialType_ThrowsOnLessThanZero() public void GetSpecialType_ThrowsOnGreaterThanCount() { var source = "class C1 { }"; - var comp = CreateCompilation(source); + var comp = (Compilation)CreateCompilation(source); var specialType = SpecialType.Count + 1; diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs index dff6ce2c6815a..3c87eff8928de 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs @@ -5451,6 +5451,8 @@ namespace System public class Object {} public class ValueType {} public struct Void {} + + public struct RuntimeTypeHandle {} } "; var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute(); @@ -5466,8 +5468,6 @@ public class Type { public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => default; } - - public struct RuntimeTypeHandle {} } "; var comp1 = CreateEmptyCompilation(source1, references: new[] { comp0Ref }, parseOptions: parseOptions); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs index eabbebd3818b7..a16c746ef12fc 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CompilationCreationTests.cs @@ -80,8 +80,7 @@ public void CorLibTypes() NamedTypeSymbol type = c1.GetSpecialType((SpecialType)i); if (i is (int)SpecialType.System_Runtime_CompilerServices_RuntimeFeature or (int)SpecialType.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute or - (int)SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute or - (int)SpecialType.System_ReadOnlySpan_T) + (int)SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute) { Assert.True(type.IsErrorType()); // Not available } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs index 503d43d3e923b..ec6de998246ec 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CorLibrary/CorTypes.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -63,6 +64,24 @@ public void PresentCorLib() { var t = msCorLibRef.GetSpecialType((SpecialType)i); Assert.Equal((SpecialType)i, t.SpecialType); + Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); + Assert.Same(msCorLibRef, t.ContainingAssembly); + if (knownMissingTypes.Contains(i)) + { + // not present on dotnet core 3.1 + Assert.Equal(TypeKind.Error, t.TypeKind); + } + else + { + Assert.NotEqual(TypeKind.Error, t.TypeKind); + } + } + + for (int i = (int)InternalSpecialType.First; i < (int)InternalSpecialType.NextAvailable; i++) + { + var t = msCorLibRef.GetSpecialType((InternalSpecialType)i); + Assert.Equal(SpecialType.None, t.SpecialType); + Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); Assert.Same(msCorLibRef, t.ContainingAssembly); if (knownMissingTypes.Contains(i)) { @@ -125,6 +144,7 @@ public void FakeCorLib() Assert.True(msCorLibRef.KeepLookingForDeclaredSpecialTypes); var t = msCorLibRef.GetSpecialType((SpecialType)i); Assert.Equal((SpecialType)i, t.SpecialType); + Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); if (t.SpecialType == SpecialType.System_Object) { @@ -133,12 +153,22 @@ public void FakeCorLib() else { Assert.Equal(TypeKind.Error, t.TypeKind); - Assert.Same(msCorLibRef, t.ContainingAssembly); } Assert.Same(msCorLibRef, t.ContainingAssembly); } + for (int i = (int)InternalSpecialType.First; i < (int)InternalSpecialType.NextAvailable; i++) + { + Assert.True(msCorLibRef.KeepLookingForDeclaredSpecialTypes); + var t = msCorLibRef.GetSpecialType((InternalSpecialType)i); + Assert.Equal(SpecialType.None, t.SpecialType); + Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); + + Assert.Equal(TypeKind.Error, t.TypeKind); + Assert.Same(msCorLibRef, t.ContainingAssembly); + } + Assert.False(msCorLibRef.KeepLookingForDeclaredSpecialTypes); } @@ -167,23 +197,38 @@ public class Object Assert.True(msCorLibRef.KeepLookingForDeclaredSpecialTypes); var t = c1.GetSpecialType((SpecialType)i); Assert.Equal((SpecialType)i, t.SpecialType); + Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); Assert.Equal(TypeKind.Error, t.TypeKind); Assert.Same(msCorLibRef, t.ContainingAssembly); } } + for (int i = (int)InternalSpecialType.First; i < (int)InternalSpecialType.NextAvailable; i++) + { + Assert.True(msCorLibRef.KeepLookingForDeclaredSpecialTypes); + var t = c1.GetSpecialType((InternalSpecialType)i); + Assert.Equal(SpecialType.None, t.SpecialType); + Assert.Equal((ExtendedSpecialType)i, t.ExtendedSpecialType); + + Assert.Equal(TypeKind.Error, t.TypeKind); + Assert.Same(msCorLibRef, t.ContainingAssembly); + } + var system_object = msCorLibRef.Modules[0].GlobalNamespace.GetMembers("System"). Select(m => (NamespaceSymbol)m).Single().GetTypeMembers("Object").Single(); Assert.Equal(SpecialType.System_Object, system_object.SpecialType); + Assert.Equal((ExtendedSpecialType)SpecialType.System_Object, system_object.ExtendedSpecialType); Assert.False(msCorLibRef.KeepLookingForDeclaredSpecialTypes); Assert.Same(system_object, c1.GetSpecialType(SpecialType.System_Object)); Assert.Throws(() => c1.GetSpecialType(SpecialType.None)); - Assert.Throws(() => c1.GetSpecialType(SpecialType.Count + 1)); + Assert.Throws(() => ((Compilation)c1).GetSpecialType(SpecialType.None)); + Assert.Throws(() => c1.GetSpecialType(InternalSpecialType.NextAvailable)); + Assert.Throws(() => ((Compilation)c1).GetSpecialType(SpecialType.Count + 1)); } [WorkItem(697521, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/697521")] @@ -225,5 +270,801 @@ internal class ArrayContract : Array // internal class ArrayContract : Array Diagnostic(ErrorCode.ERR_DeriveFromEnumOrValueType, "Array").WithArguments("System.ArrayContract", "System.Array")); } + + [Fact] + public void System_Type__WellKnownVsSpecial_01() + { + var source = @" +class Program +{ + static void Main() + { + var x = typeof(Program); + System.Console.WriteLine(x); + } +} +"; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.MakeMemberMissing(WellKnownMember.System_Type__GetTypeFromHandle); + + Assert.False(comp.GetSpecialType(InternalSpecialType.System_Type).IsErrorType()); + + var tree = comp.SyntaxTrees.Single(); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var model = comp.GetSemanticModel(tree); + + Assert.Equal(InternalSpecialType.System_Type, model.GetTypeInfo(node).Type.GetSymbol().ExtendedSpecialType); + + CompileAndVerify(comp, expectedOutput: "Program"); + + comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.MakeMemberMissing(SpecialMember.System_Type__GetTypeFromHandle); + comp.VerifyEmitDiagnostics( + // (6,17): error CS0656: Missing compiler required member 'System.Type.GetTypeFromHandle' + // var x = typeof(Program); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "typeof(Program)").WithArguments("System.Type", "GetTypeFromHandle").WithLocation(6, 17) + ); + } + + [Fact] + public void System_Type__WellKnownVsSpecial_02() + { + var corLib_v1 = @" +namespace System +{ + public class Object + {} + + public class Void + {} + + public class ValueType + {} + + public struct RuntimeTypeHandle + {} +} +"; + var corLib_v1_Comp = CreateEmptyCompilation(corLib_v1, assemblyName: "corLib"); + + var typeLib_v1 = @" +namespace System +{ + public class Type + { + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null; + } +} +"; + + var typeLib_v1_Comp = CreateEmptyCompilation(typeLib_v1, references: [corLib_v1_Comp.ToMetadataReference()], assemblyName: "typeLib"); + + var source1 = @" +#nullable disable + +public class Test +{ + public static System.Type TypeOf() => typeof(Test); +} +"; + var comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + Assert.True(comp1.GetSpecialType(InternalSpecialType.System_Type).IsErrorType()); + comp1.MakeMemberMissing(SpecialMember.System_Type__GetTypeFromHandle); + + var tree = comp1.SyntaxTrees.Single(); + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var model = comp1.GetSemanticModel(tree); + + Assert.Equal((ExtendedSpecialType)0, model.GetTypeInfo(node).Type.GetSymbol().ExtendedSpecialType); + + var comp1Ref = comp1.EmitToImageReference(); + + var corLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Object))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(void))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.RuntimeTypeHandle))] +"; + var corLib_v2_Comp = CreateCompilation(corLib_v2, assemblyName: "corLib"); + + var typeLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Type))] +"; + + var typeLib_v2_Comp = CreateCompilation(typeLib_v2, assemblyName: "typeLib"); + + var source2 = @" +class Program +{ + static void Main() + { + System.Console.WriteLine(Test.TypeOf()); + } +} +"; + + var comp = CreateCompilation(source2, references: [corLib_v2_Comp.ToMetadataReference(), typeLib_v2_Comp.ToMetadataReference(), comp1Ref], options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "Test"); + + comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + comp1.MakeMemberMissing(WellKnownMember.System_Type__GetTypeFromHandle); + comp1.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // (6,43): error CS0656: Missing compiler required member 'System.Type.GetTypeFromHandle' + // public static System.Type TypeOf() => typeof(Test); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "typeof(Test)").WithArguments("System.Type", "GetTypeFromHandle").WithLocation(6, 43) + ); + } + + [Fact] + public void System_Type__WellKnownVsSpecial_03() + { + var source = @" +record R +{ + public static System.Type TypeOf() => new R().EqualityContract; +} + +class Program +{ + static void Main() + { + System.Console.WriteLine(R.TypeOf()); + } +} +"; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.MakeMemberMissing(WellKnownMember.System_Type__GetTypeFromHandle); + + Assert.False(comp.GetSpecialType(InternalSpecialType.System_Type).IsErrorType()); + + CompileAndVerify(comp, expectedOutput: "R"); + + comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.MakeMemberMissing(SpecialMember.System_Type__GetTypeFromHandle); + comp.VerifyEmitDiagnostics( + // (2,1): error CS0656: Missing compiler required member 'System.Type.GetTypeFromHandle' + // record R + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"record R +{ + public static System.Type TypeOf() => new R().EqualityContract; +}").WithArguments("System.Type", "GetTypeFromHandle").WithLocation(2, 1) + ); + } + + [Fact] + public void System_Type__WellKnownVsSpecial_04() + { + var corLib_v1 = @" +namespace System +{ + public class Object + { + public virtual string ToString() => null; + public virtual int GetHashCode() => 0; + public virtual bool Equals(object obj) => false; + } + + public class Void + {} + + public class ValueType + {} + + public struct RuntimeTypeHandle + {} + + public struct Byte + {} + + public struct Int32 + {} + + public struct Boolean + {} + + public class String + {} + + public interface IEquatable + { + bool Equals(T other); + } + + public class Attribute + {} + + public enum AttributeTargets + { + } + public class AttributeUsageAttribute + { + public AttributeUsageAttribute(AttributeTargets validOn){} + public bool AllowMultiple => false; + public bool Inherited => false; + } + + public class Exception + {} + + namespace Text + { + public sealed class StringBuilder + {} + + } +} +"; + var corLib_v1_Comp = CreateEmptyCompilation(corLib_v1, assemblyName: "corLib"); + + var typeLib_v1 = @" +namespace System +{ + public class Type + { + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null; + } +} +"; + + var typeLib_v1_Comp = CreateEmptyCompilation(typeLib_v1, references: [corLib_v1_Comp.ToMetadataReference()], assemblyName: "typeLib"); + + var source1 = @" +#nullable disable + +sealed public record R +{ + public static System.Type TypeOf() => new R().EqualityContract; + public override string ToString() => null; + public override int GetHashCode() => 0; + public bool Equals(R obj) => false; +} +"; + var comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + Assert.True(comp1.GetSpecialType(InternalSpecialType.System_Type).IsErrorType()); + comp1.MakeMemberMissing(SpecialMember.System_Type__GetTypeFromHandle); + + var comp1Ref = comp1.EmitToImageReference(); + + var corLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Object))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(void))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.RuntimeTypeHandle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Byte))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Int32))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Boolean))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.String))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Attribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.AttributeTargets))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.AttributeUsageAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Exception))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.IEquatable<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Text.StringBuilder))] +"; + var corLib_v2_Comp = CreateCompilation(corLib_v2, assemblyName: "corLib"); + + var typeLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Type))] +"; + + var typeLib_v2_Comp = CreateCompilation(typeLib_v2, assemblyName: "typeLib"); + + var source2 = @" +class Program +{ + static void Main() + { + System.Console.WriteLine(R.TypeOf()); + } +} +"; + + var comp = CreateCompilation(source2, references: [corLib_v2_Comp.ToMetadataReference(), typeLib_v2_Comp.ToMetadataReference(), comp1Ref], options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "R"); + + comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + comp1.MakeMemberMissing(WellKnownMember.System_Type__GetTypeFromHandle); + comp1.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // (4,1): error CS0656: Missing compiler required member 'System.Type.GetTypeFromHandle' + // sealed public record R + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"sealed public record R +{ + public static System.Type TypeOf() => new R().EqualityContract; + public override string ToString() => null; + public override int GetHashCode() => 0; + public bool Equals(R obj) => false; +}").WithArguments("System.Type", "GetTypeFromHandle").WithLocation(4, 1) + ); + } + + [Fact] + public void CreateDelegate__MethodInfoVsDelegate_01() + { + var source = @" +class Program +{ + static void Main() + { + System.Linq.Expressions.Expression> x = () => C1.M1; + System.Console.WriteLine(x); + } +} + +class C1 +{ + public static void M1() {} +} +"; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Mscorlib40AndSystemCore, options: TestOptions.DebugExe); + comp.MakeMemberMissing(WellKnownMember.System_Reflection_MethodInfo__CreateDelegate); + comp.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle2); + comp.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle); + comp.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle2); + + CompileAndVerify(comp, expectedOutput: "() => Convert(CreateDelegate(System.Action, null, Void M1())" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", Action" : "") + + ")"); + + comp = CreateCompilation(source, targetFramework: TargetFramework.Mscorlib40AndSystemCore, options: TestOptions.DebugExe); + comp.MakeMemberMissing(SpecialMember.System_Delegate__CreateDelegate); + comp.VerifyEmitDiagnostics( + // (6,82): error CS0656: Missing compiler required member 'System.Delegate.CreateDelegate' + // System.Linq.Expressions.Expression> x = () => C1.M1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "C1.M1").WithArguments("System.Delegate", "CreateDelegate").WithLocation(6, 82) + ); + + comp = CreateCompilation(source, targetFramework: TargetFramework.Mscorlib40AndSystemCore, options: TestOptions.DebugExe); + comp.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle); + comp.VerifyEmitDiagnostics( + // (6,82): error CS0656: Missing compiler required member 'System.Reflection.MethodBase.GetMethodFromHandle' + // System.Linq.Expressions.Expression> x = () => C1.M1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "C1.M1").WithArguments("System.Reflection.MethodBase", "GetMethodFromHandle").WithLocation(6, 82) + ); + } + + [Fact] + public void CreateDelegate__MethodInfoVsDelegate_02() + { + var source = @" +class Program +{ + static void Main() + { + System.Linq.Expressions.Expression> x = () => C1.M1; + System.Console.WriteLine(x); + } +} + +class C1 +{ + public static void M1() {} +} +"; + + var comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.MakeMemberMissing(SpecialMember.System_Delegate__CreateDelegate); + comp.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle); + comp.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle2); + + CompileAndVerify( + comp, expectedOutput: "() => Convert(Void M1().CreateDelegate(System.Action, null)" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", Action" : "") + + ")"); + + comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle2); + comp.VerifyEmitDiagnostics( + // (6,82): error CS0656: Missing compiler required member 'System.Reflection.MethodBase.GetMethodFromHandle' + // System.Linq.Expressions.Expression> x = () => C1.M1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "C1.M1").WithArguments("System.Reflection.MethodBase", "GetMethodFromHandle").WithLocation(6, 82) + ); + } + + [Fact] + public void GetMethodFromHandle_WellKnown_01() + { + var corLib_v1 = @" +namespace System +{ + public class Object + {} + + public class Void + {} + + public class ValueType + {} + + public struct RuntimeTypeHandle + {} + + public struct RuntimeMethodHandle + {} + + public struct Int32 + {} + + public abstract class Delegate + {} + + public abstract class MulticastDelegate : Delegate + {} + + public delegate void Action(); + + public delegate TResult Func(); + + public struct Nullable + {} +} + +namespace System.Collections.Generic +{ + public interface IEnumerable + {} +} +"; + var corLib_v1_Comp = CreateEmptyCompilation(corLib_v1, assemblyName: "corLib"); + + var typeLib_v1 = @" +namespace System +{ + public class Type + { + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null; + } +} +namespace System.Reflection +{ + public abstract partial class MethodBase + { + public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => null; + } + + public abstract partial class MethodInfo : MethodBase + { + public virtual Delegate CreateDelegate(Type delegateType) => null; + public virtual Delegate CreateDelegate(Type delegateType, object target) => null; + } +} + +namespace System.Linq.Expressions +{ + public abstract class Expression + { + public static ConstantExpression Constant (object value) => null; + public static ConstantExpression Constant (object value, Type type) => null; + + public static MethodCallExpression Call (Expression instance, System.Reflection.MethodInfo method, Expression[] arguments) => null; + + public static UnaryExpression Convert (Expression expression, Type type) => null; + public static UnaryExpression Convert (Expression expression, Type type, System.Reflection.MethodInfo method) => null; + + public static Expression Lambda (Expression body, ParameterExpression[] parameters) => null; + } + + public abstract class LambdaExpression : Expression + {} + + public abstract class Expression : LambdaExpression + {} + + public class ConstantExpression : Expression + {} + + public class ParameterExpression : Expression + {} + + public class MethodCallExpression : Expression + {} + + public sealed class UnaryExpression : Expression + {} +} +"; + + var typeLib_v1_Comp = CreateEmptyCompilation(typeLib_v1, references: [corLib_v1_Comp.ToMetadataReference()], assemblyName: "typeLib"); + + typeLib_v1_Comp.VerifyDiagnostics(); + + var source1 = @" +#nullable disable + +public class Test +{ + public static System.Linq.Expressions.Expression> Expression() + { + return () => C1.M1; + } +} + +class C1 +{ + public static void M1() {} +} +"; + var comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + comp1.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle); + comp1.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle2); + comp1.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle2); + + var comp1Ref = comp1.EmitToImageReference(); + + var corLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Object))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(void))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.RuntimeTypeHandle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.RuntimeMethodHandle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Int32))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Func<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Nullable<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.IEnumerable<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Delegate))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.MulticastDelegate))] +"; + var corLib_v2_Comp = CreateCompilation(corLib_v2, assemblyName: "corLib"); + + var typeLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Type))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.MethodBase))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.MethodInfo))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.Expression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.LambdaExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.Expression<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.ConstantExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.ParameterExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.MethodCallExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.UnaryExpression))] +"; + + var typeLib_v2_Comp = CreateCompilation(typeLib_v2, assemblyName: "typeLib"); + + var source2 = @" +class Program +{ + static void Main() + { + System.Console.WriteLine(Test.Expression()); + } +} +"; + + var comp = CreateCompilation(source2, references: [corLib_v2_Comp.ToMetadataReference(), typeLib_v2_Comp.ToMetadataReference(), comp1Ref], options: TestOptions.DebugExe); + + CompileAndVerify(comp, expectedOutput: "() => Convert(Void M1().CreateDelegate(System.Action, null)" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", Action" : "") + + ")"); + + comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + comp1.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle); + comp1.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // (8,22): error CS0656: Missing compiler required member 'System.Reflection.MethodBase.GetMethodFromHandle' + // return () => C1.M1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "C1.M1").WithArguments("System.Reflection.MethodBase", "GetMethodFromHandle").WithLocation(8, 22) + ); + } + + [Fact] + public void GetMethodFromHandle_WellKnown_02() + { + var corLib_v1 = @" +namespace System +{ + public class Object + {} + + public class Void + {} + + public class ValueType + {} + + public struct RuntimeTypeHandle + {} + + public struct RuntimeMethodHandle + {} + + public struct Int32 + {} + + public abstract class Delegate + {} + + public abstract class MulticastDelegate : Delegate + {} + + public delegate void Action(); + + public delegate TResult Func(); + + public struct Nullable + {} +} + +namespace System.Collections.Generic +{ + public interface IEnumerable + {} +} +"; + var corLib_v1_Comp = CreateEmptyCompilation(corLib_v1, assemblyName: "corLib"); + + var typeLib_v1 = @" +namespace System +{ + public class Type + { + public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null; + } +} +namespace System.Reflection +{ + public abstract partial class MethodBase + { + public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => null; + public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) => null; + } + + public abstract partial class MethodInfo : MethodBase + { + public virtual Delegate CreateDelegate(Type delegateType) => null; + public virtual Delegate CreateDelegate(Type delegateType, object target) => null; + } +} + +namespace System.Linq.Expressions +{ + public abstract class Expression + { + public static ConstantExpression Constant (object value) => null; + public static ConstantExpression Constant (object value, Type type) => null; + + public static MethodCallExpression Call (Expression instance, System.Reflection.MethodInfo method, Expression[] arguments) => null; + + public static UnaryExpression Convert (Expression expression, Type type) => null; + public static UnaryExpression Convert (Expression expression, Type type, System.Reflection.MethodInfo method) => null; + + public static Expression Lambda (Expression body, ParameterExpression[] parameters) => null; + } + + public abstract class LambdaExpression : Expression + {} + + public abstract class Expression : LambdaExpression + {} + + public class ConstantExpression : Expression + {} + + public class ParameterExpression : Expression + {} + + public class MethodCallExpression : Expression + {} + + public sealed class UnaryExpression : Expression + {} +} +"; + + var typeLib_v1_Comp = CreateEmptyCompilation(typeLib_v1, references: [corLib_v1_Comp.ToMetadataReference()], assemblyName: "typeLib"); + + typeLib_v1_Comp.VerifyDiagnostics(); + + var source1 = @" +#nullable disable + +public class Test +{ + public static System.Linq.Expressions.Expression> Expression() + { + return () => C1.M1; + } +} + +class C1 +{ + public static void M1() {} +} +"; + var comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + comp1.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle); + comp1.MakeMemberMissing(SpecialMember.System_Reflection_MethodBase__GetMethodFromHandle2); + + var comp1Ref = comp1.EmitToImageReference(); + + var corLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Object))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(void))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.ValueType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.RuntimeTypeHandle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.RuntimeMethodHandle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Int32))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Action))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Func<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Nullable<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.IEnumerable<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Delegate))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.MulticastDelegate))] +"; + var corLib_v2_Comp = CreateCompilation(corLib_v2, assemblyName: "corLib"); + + var typeLib_v2 = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Type))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.MethodBase))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Reflection.MethodInfo))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.Expression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.LambdaExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.Expression<>))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.ConstantExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.ParameterExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.MethodCallExpression))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Linq.Expressions.UnaryExpression))] +"; + + var typeLib_v2_Comp = CreateCompilation(typeLib_v2, assemblyName: "typeLib"); + + var source2 = @" +class Program +{ + static void Main() + { + System.Console.WriteLine(Test.Expression()); + } +} +"; + + var comp = CreateCompilation(source2, references: [corLib_v2_Comp.ToMetadataReference(), typeLib_v2_Comp.ToMetadataReference(), comp1Ref], options: TestOptions.DebugExe); + + CompileAndVerify(comp, expectedOutput: "() => Convert(Void M1().CreateDelegate(System.Action, null)" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", Action" : "") + + ")"); + + comp1 = CreateEmptyCompilation( + source1, references: [corLib_v1_Comp.ToMetadataReference(), typeLib_v1_Comp.ToMetadataReference()], + parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute()); + + comp1.MakeMemberMissing(WellKnownMember.System_Reflection_MethodBase__GetMethodFromHandle2); + comp1.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // (8,22): error CS0656: Missing compiler required member 'System.Reflection.MethodBase.GetMethodFromHandle' + // return () => C1.M1; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "C1.M1").WithArguments("System.Reflection.MethodBase", "GetMethodFromHandle").WithLocation(8, 22) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/LoadingFields.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/LoadingFields.cs index 6b426817b160f..5130bdd3eefc2 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/LoadingFields.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/LoadingFields.cs @@ -146,8 +146,10 @@ public void TestLoadFieldsOfReadOnlySpanFromCorlib() { var comp = CreateCompilation("", targetFramework: TargetFramework.Net60); - var readOnlySpanType = comp.GetSpecialType(SpecialType.System_ReadOnlySpan_T); + var readOnlySpanType = comp.GetSpecialType(InternalSpecialType.System_ReadOnlySpan_T); Assert.False(readOnlySpanType.IsErrorType()); + Assert.Equal(SpecialType.None, readOnlySpanType.SpecialType); + Assert.Equal((ExtendedSpecialType)InternalSpecialType.System_ReadOnlySpan_T, readOnlySpanType.ExtendedSpecialType); var fields = readOnlySpanType.GetMembers().OfType(); Assert.NotEmpty(fields); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index 645100119fac1..132f3bcfa02b7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -532,8 +532,7 @@ public void AllSpecialTypes() if (special is SpecialType.System_Runtime_CompilerServices_RuntimeFeature or SpecialType.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute or - SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute or - SpecialType.System_ReadOnlySpan_T) + SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute) { Assert.Equal(SymbolKind.ErrorType, symbol.Kind); // Not available } @@ -567,7 +566,8 @@ public void AllSpecialTypeMembers() || special == SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefFields || special == SpecialMember.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute__ctor || special == SpecialMember.System_Runtime_CompilerServices_InlineArrayAttribute__ctor - || special == SpecialMember.System_ReadOnlySpan_T__ctor_Reference) + || special == SpecialMember.System_ReadOnlySpan_T__ctor_Reference + || special == SpecialMember.System_Array__Empty) { Assert.Null(symbol); // Not available } @@ -692,7 +692,6 @@ public void AllWellKnownTypesBeforeCSharp7() { foreach (var type in new[] { WellKnownType.System_Math, - WellKnownType.System_Array, WellKnownType.System_Attribute, WellKnownType.System_CLSCompliantAttribute, WellKnownType.System_Convert, @@ -702,9 +701,6 @@ public void AllWellKnownTypesBeforeCSharp7() WellKnownType.System_FormattableString, WellKnownType.System_Guid, WellKnownType.System_IFormattable, - WellKnownType.System_RuntimeTypeHandle, - WellKnownType.System_RuntimeFieldHandle, - WellKnownType.System_RuntimeMethodHandle, WellKnownType.System_MarshalByRefObject, WellKnownType.System_Type, WellKnownType.System_Reflection_AssemblyKeyFileAttribute, @@ -912,15 +908,14 @@ public void AllWellKnownTypesBeforeCSharp7() WellKnownType.System_Environment, - WellKnownType.System_Runtime_GCLatencyMode, - WellKnownType.System_IFormatProvider } + WellKnownType.System_Runtime_GCLatencyMode} ) { Assert.True(type <= WellKnownType.CSharp7Sentinel); } - // There were 205 well-known types prior to CSharp7 - Assert.Equal(205, (int)(WellKnownType.CSharp7Sentinel - WellKnownType.First)); + // There were 200 well-known types prior to CSharp7 + Assert.Equal(200, (int)(WellKnownType.CSharp7Sentinel - WellKnownType.First)); } [Fact] @@ -952,7 +947,6 @@ public void AllWellKnownTypeMembers() case WellKnownMember.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators__CompareStringStringStringBoolean: // C# can't embed VB core. continue; - case WellKnownMember.System_Array__Empty: case WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte: case WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags: case WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor: diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs index a196e4e488465..42d03387f3ce3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs @@ -50,7 +50,7 @@ public override ImmutableArray Locations get { return ImmutableArray.Create(); } } - internal override NamedTypeSymbol GetDeclaredSpecialType(SpecialType type) + internal override NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type) { throw new NotImplementedException(); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs index d96d72871507c..d8031c75dcea4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -4143,10 +4143,10 @@ class C var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (8,19): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (8,19): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public string P2 { get; set; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P2").WithArguments("property", "P2").WithLocation(8, 19), - // (9,19): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (9,19): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public string F2; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "F2").WithArguments("field", "F2").WithLocation(9, 19) ); @@ -4175,16 +4175,16 @@ public C(int _) {} var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (11,12): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (11,12): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "P2").WithLocation(11, 12), - // (11,12): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (11,12): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "F2").WithLocation(11, 12), - // (15,12): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C(int _) {} Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "P2").WithLocation(15, 12), - // (15,12): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (15,12): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C(int _) {} Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "F2").WithLocation(15, 12) ); @@ -4229,10 +4229,10 @@ public S() {} var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (11,12): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (11,12): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public S() {} Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("property", "P2").WithLocation(11, 12), - // (11,12): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (11,12): warning CS8618: Non-nullable field 'F2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public S() {} Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "F2").WithLocation(11, 12) ); @@ -4368,10 +4368,10 @@ public C() { } var comp = CreateCompilationWithRequiredMembers(new[] { code, MemberNotNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Property").WithLocation(9, 12), - // (9,12): warning CS8618: Non-nullable field '_field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (9,12): warning CS8618: Non-nullable field '_field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "_field").WithLocation(9, 12) ); @@ -4396,10 +4396,10 @@ public C() { } var comp = CreateCompilationWithRequiredMembers(new[] { code, MemberNotNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Property").WithLocation(9, 12), - // (9,12): warning CS8618: Non-nullable field '_field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (9,12): warning CS8618: Non-nullable field '_field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "_field").WithLocation(9, 12) ); @@ -4447,7 +4447,7 @@ public C() { } var comp = CreateCompilationWithRequiredMembers(new[] { code, MemberNotNullAttributeDefinition }); comp.VerifyDiagnostics( - // (8,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (8,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop3").WithLocation(8, 12) ); @@ -4607,10 +4607,10 @@ public C(bool unused) : this() var comp = CreateCompilationWithRequiredMembers(new[] { code, MemberNotNullAttributeDefinition }); comp.VerifyDiagnostics( - // (9,12): warning CS8618: Non-nullable property 'Property2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'Property2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Property2").WithLocation(9, 12), - // (9,12): warning CS8618: Non-nullable property 'Property1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,12): warning CS8618: Non-nullable property 'Property1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Property1").WithLocation(9, 12) ); @@ -4782,7 +4782,7 @@ private Derived() // (4,20): warning CS0169: The field 'Derived._field' is never used // private string _field; Diagnostic(ErrorCode.WRN_UnreferencedField, "_field").WithArguments("Derived._field").WithLocation(4, 20), - // (5,13): warning CS8618: Non-nullable field '_field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (5,13): warning CS8618: Non-nullable field '_field' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // private Derived() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("field", "_field").WithLocation(5, 13) }; @@ -4825,7 +4825,7 @@ public Derived() """; var expectedDiagnostics = new[] { - // (6,12): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (6,12): warning CS8618: Non-nullable property 'Property' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Property").WithLocation(6, 12), // (8,9): warning CS8602: Dereference of a possibly null reference. @@ -5131,7 +5131,7 @@ public Derived() : base() // 2 var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (9,15): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,15): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // protected Base() {} // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Base").WithArguments("property", "Prop1").WithLocation(9, 15), // (17,24): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. @@ -5195,16 +5195,16 @@ public Derived() : this(0) var comp = CreateCompilationWithRequiredMembers(new[] { derived, @base }); comp.VerifyDiagnostics( - // (10,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop3").WithLocation(10, 12), - // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(10, 12), - // (16,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop3").WithLocation(16, 12), - // (16,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(16, 12), // (21,24): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. @@ -5215,16 +5215,16 @@ public Derived() : this(0) var baseComp = CreateCompilationWithRequiredMembers(@base); comp = CreateCompilation(derived, new[] { baseComp.EmitToImageReference() }); comp.VerifyDiagnostics( - // (10,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop3").WithLocation(10, 12), - // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(10, 12), - // (16,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,12): warning CS8618: Non-nullable property 'Prop3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop3").WithLocation(16, 12), - // (16,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (16,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(16, 12), // (21,24): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. @@ -5277,16 +5277,16 @@ public Derived() : this(0) var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (17,12): warning CS8618: Non-nullable field 'Field3' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (17,12): warning CS8618: Non-nullable field 'Field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("field", "Field3").WithLocation(17, 12), - // (17,12): warning CS8618: Non-nullable field 'Field1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (17,12): warning CS8618: Non-nullable field 'Field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("field", "Field1").WithLocation(17, 12), - // (23,12): warning CS8618: Non-nullable field 'Field3' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (23,12): warning CS8618: Non-nullable field 'Field3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("field", "Field3").WithLocation(23, 12), - // (23,12): warning CS8618: Non-nullable field 'Field1' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (23,12): warning CS8618: Non-nullable field 'Field1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("field", "Field1").WithLocation(23, 12), // (28,24): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. @@ -5341,16 +5341,16 @@ public Derived() : this(0) var comp = CreateCompilationWithRequiredMembers(new[] { derived, @base }); comp.VerifyDiagnostics( - // (10,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop2").WithLocation(10, 12), - // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(10, 12), - // (15,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop2").WithLocation(15, 12), - // (15,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(15, 12), // (19,24): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. @@ -5361,16 +5361,16 @@ public Derived() : this(0) var baseComp = CreateCompilationWithRequiredMembers(@base); comp = CreateCompilation(derived, new[] { baseComp.EmitToImageReference() }); comp.VerifyDiagnostics( - // (10,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop2").WithLocation(10, 12), - // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (10,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(int unused) : base() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(10, 12), - // (15,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'Prop2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop2").WithLocation(15, 12), - // (15,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (15,12): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public Derived(bool unused) Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Derived").WithArguments("property", "Prop1").WithLocation(15, 12), // (19,24): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. @@ -5732,7 +5732,7 @@ public record Derived() : Base; var comp = CreateCompilationWithRequiredMembers(code); comp.VerifyDiagnostics( - // (9,15): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (9,15): warning CS8618: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // protected Base() {} // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Base").WithArguments("property", "Prop1").WithLocation(9, 15), // (12,15): error CS9039: This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute. diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ClsComplianceTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ClsComplianceTests.cs index 6b409bd315dd5..3a8e532ab7d36 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ClsComplianceTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/ClsComplianceTests.cs @@ -3165,7 +3165,6 @@ public class C case SpecialType.System_Runtime_CompilerServices_RuntimeFeature: // static and not available case SpecialType.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute: // not available case SpecialType.System_Runtime_CompilerServices_InlineArrayAttribute: // not available - case SpecialType.System_ReadOnlySpan_T: // not available continue; } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs index 75ebd9324b0cc..afdf7d50836b5 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs @@ -1557,7 +1557,7 @@ public C() var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (5,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // (5,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. // public C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(5, 12)); @@ -1651,7 +1651,7 @@ static void Main() var comp = CreateCompilation(source, options: WithNullableEnable()); comp.VerifyDiagnostics( - // (6,27): warning CS8618: Non-nullable field 's_data' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // (6,27): warning CS8618: Non-nullable field 's_data' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. // private static string s_data; Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "s_data").WithArguments("field", "s_data").WithLocation(6, 27), // (6,27): warning CS0649: Field 'C.s_data' is never assigned to, and will always have its default value null diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalErrorTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalErrorTests.cs index 337c50884f755..64fc656f659fb 100644 --- a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalErrorTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalErrorTests.cs @@ -572,6 +572,51 @@ public static int Main () Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 25)); } + [Fact] + public void CS1035AtColonParsedAsComment_01() + { + var test = """ + var x = @:; + """; + + ParsingTests.ParseAndValidate(test, + // (1,9): error CS1056: Unexpected character '@' + // var x = @:; + Diagnostic(ErrorCode.ERR_UnexpectedCharacter, "@").WithArguments("@").WithLocation(1, 9), + // (1,12): error CS1733: Expected expression + // var x = @:; + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(1, 12), + // (1,12): error CS1002: ; expected + // var x = @:; + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 12)); + } + + [Fact] + public void CS1035AtColonParsedAsComment_02() + { + var test = """ + @:
test
+ """; + + ParsingTests.ParseAndValidate(test, + // (1,1): error CS1056: Unexpected character '@' + // @:
test
+ Diagnostic(ErrorCode.ERR_UnexpectedCharacter, "@").WithArguments("@").WithLocation(1, 1)); + } + + [Fact] + public void CS1035AtColonParsedAsComment_03() + { + var test = """ + @: M() {} + """; + + ParsingTests.ParseAndValidate(test, + // (1,1): error CS1056: Unexpected character '@' + // @: M() {} + Diagnostic(ErrorCode.ERR_UnexpectedCharacter, "@").WithArguments("@").WithLocation(1, 1)); + } + [Fact, WorkItem(526993, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/526993")] public void CS1039ERR_UnterminatedStringLit() { diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs index 85afeca5a8582..462c92901099f 100644 --- a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs @@ -440,6 +440,143 @@ public void TestMixedMultiLineCommentTerminators_01(char outsideDelimiter, char Assert.Equal(SyntaxKind.MultiLineCommentTrivia, trivia[0].Kind()); } + [Fact] + [Trait("Feature", "Comments")] + public void TestAtColonTreatedAsComment_RazorRecovery() + { + var text = "@: More text"; + var token = LexToken(text); + + Assert.NotEqual(default, token); + Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind()); + Assert.Equal(text, token.ToFullString()); + var errors = token.Errors(); + errors.Verify( + // error CS1056: Unexpected character '@' + TestBase.Diagnostic(ErrorCode.ERR_UnexpectedCharacter).WithArguments("@").WithLocation(1, 1)); + var trivia = token.GetLeadingTrivia().ToArray(); + Assert.Equal(1, trivia.Length); + Assert.NotEqual(default, trivia[0]); + Assert.Equal(SyntaxKind.SingleLineCommentTrivia, trivia[0].Kind()); + } + + [Fact] + [Trait("Feature", "Comments")] + public void TestAtColonTreatedAsCommentAsTrailingTrivia_RazorRecovery() + { + var text = """ + Identifier @: More text + // Regular comment + SecondIdentifier + """; + var tokens = Lex(text).ToList(); + var token = tokens[0]; + + Assert.NotEqual(default, token); + Assert.Equal(SyntaxKind.IdentifierToken, token.Kind()); + var errors = token.Errors(); + errors.Verify( + // error CS1056: Unexpected character '@' + TestBase.Diagnostic(ErrorCode.ERR_UnexpectedCharacter).WithArguments("@").WithLocation(1, 1)); + var trivia = token.GetLeadingTrivia().ToArray(); + Assert.Equal(0, trivia.Length); + trivia = token.GetTrailingTrivia().ToArray(); + Assert.Equal(3, trivia.Length); + Assert.NotEqual(default, trivia[1]); + Assert.Equal(SyntaxKind.SingleLineCommentTrivia, trivia[1].Kind()); + Assert.Equal("@: More text", trivia[1].ToFullString()); + + token = tokens[1]; + Assert.NotEqual(default, token); + Assert.Equal(SyntaxKind.IdentifierToken, token.Kind()); + Assert.Equal(""" + // Regular comment + SecondIdentifier + """, token.ToFullString()); + } + + [Fact] + [Trait("Feature", "Comments")] + public void TestAtColonTreatedAsComment_TrailingMultiLine_RazorRecovery() + { + var text = """ + @: /* + Identifier + */ + """; + + var tokens = Lex(text).ToList(); + var token = tokens[0]; + + Assert.NotEqual(default, token); + Assert.Equal(SyntaxKind.IdentifierToken, token.Kind()); + Assert.Equal(""" + @: /* + Identifier + + """, token.ToFullString()); + var errors = token.Errors(); + errors.Verify( + // error CS1056: Unexpected character '@' + TestBase.Diagnostic(ErrorCode.ERR_UnexpectedCharacter).WithArguments("@").WithLocation(1, 1)); + var trivia = token.GetLeadingTrivia().ToArray(); + Assert.Equal(2, trivia.Length); + Assert.NotEqual(default, trivia[0]); + Assert.Equal(SyntaxKind.SingleLineCommentTrivia, trivia[0].Kind()); + Assert.Equal("@: /*", trivia[0].ToFullString()); + } + + [Fact] + [Trait("Feature", "Comments")] + public void TestAtColonTreatedAsComment_PreprocessorDisabled_RazorRecovery() + { + var text = """ + #if false + @: + #endif + """; + + var token = LexToken(text); + + Assert.NotEqual(default, token); + Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind()); + Assert.Equal(text, token.ToFullString()); + var errors = token.Errors(); + errors.Verify(); + var trivia = token.GetLeadingTrivia().ToArray(); + Assert.Equal(3, trivia.Length); + Assert.Equal(SyntaxKind.IfDirectiveTrivia, trivia[0].Kind()); + Assert.Equal(SyntaxKind.DisabledTextTrivia, trivia[1].Kind()); + Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, trivia[2].Kind()); + } + + [Fact] + [Trait("Feature", "Comments")] + public void TestAtColonTreatedAsComment_PreprocessorEnabled_RazorRecovery() + { + var text = """ + #if true + @: + #endif + """; + + var token = LexToken(text); + + Assert.NotEqual(default, token); + Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind()); + Assert.Equal(text, token.ToFullString()); + var errors = token.Errors(); + errors.Verify( + // error CS1056: Unexpected character '@' + TestBase.Diagnostic(ErrorCode.ERR_UnexpectedCharacter).WithArguments("@").WithLocation(1, 1)); + var trivia = token.GetLeadingTrivia().ToArray(); + Assert.Equal(4, trivia.Length); + Assert.Equal(SyntaxKind.IfDirectiveTrivia, trivia[0].Kind()); + Assert.Equal(SyntaxKind.SingleLineCommentTrivia, trivia[1].Kind()); + Assert.Equal(SyntaxKind.EndOfLineTrivia, trivia[2].Kind()); + Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, trivia[3].Kind()); + } + [Fact] [Trait("Feature", "Comments")] public void TestCommentWithTextWindowSentinel() diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/SyntaxTokenParserTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/SyntaxTokenParserTests.cs new file mode 100644 index 0000000000000..a943d55c7d1d5 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/SyntaxTokenParserTests.cs @@ -0,0 +1,276 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.LexicalAndXml; + +public class SyntaxTokenParserTests +{ + [Fact] + public void TestDispose() + { + var sourceText = SourceText.From("class C { }"); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + parser.Dispose(); + Assert.Throws(() => parser.ParseNextToken()); + parser.Dispose(); + } + + [Fact] + public void TestParseNext() + { + var sourceText = SourceText.From(""" + // Hello world + class C + { + + } + """.NormalizeLineEndings()); + + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(0, 22), """ + // Hello world + class + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.None, new TextSpan(22, 3), """ + C + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.OpenBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(25, 3), """ + { + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.CloseBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(28, 3), """ + + } + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(31, 0), "", parser.ParseNextToken()); + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(31, 0), "", parser.ParseNextToken()); + } + + [Fact] + public void DocumentCommentTriviaIsHandled() + { + var sourceText = SourceText.From(""" + /// + /// Hello world + /// + class C + { + + } + """.NormalizeLineEndings()); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + + var result = parser.ParseNextToken(); + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(0, 54), """ + /// + /// Hello world + /// + class + """, result); + + var docCommentTrivia = result.Token.GetLeadingTrivia()[0]; + Assert.Equal(SyntaxKind.SingleLineDocumentationCommentTrivia, docCommentTrivia.Kind()); + Assert.NotNull(docCommentTrivia.GetStructure()); + + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.None, new TextSpan(54, 3), """ + C + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.OpenBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(57, 3), """ + { + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.CloseBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(60, 3), """ + + } + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(63, 0), "", parser.ParseNextToken()); + } + + [Fact] + public void DirectiveContextIsPreservedAcrossParseNextToken() + { + var sourceText = SourceText.From(""" + #if true + class C + { + + #else + } + #endif + """.NormalizeLineEndings()); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(0, 16), """ + #if true + class + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.None, new TextSpan(16, 3), """ + C + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.OpenBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(19, 3), """ + { + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(22, 18), """ + + #else + } + #endif + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(40, 0), "", parser.ParseNextToken()); + } + + [Fact] + public void SkipForwardTo() + { + var sourceText = SourceText.From(""" + This is not C# + + // Hello world + class C + { + + } + """.NormalizeLineEndings()); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + + parser.SkipForwardTo(18); + + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(18, 22), """ + // Hello world + class + """, parser.ParseNextToken()); + + parser.SkipForwardTo(43); + + AssertToken(expectedKind: SyntaxKind.OpenBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(43, 3), """ + { + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.CloseBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(46, 3), """ + + } + """, parser.ParseNextToken()); + } + + [Fact] + public void SkipForwardTo2() + { + var sourceText = SourceText.From("""class"""); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + parser.SkipForwardTo(1); + + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.None, new TextSpan(1, 4), """lass""", parser.ParseNextToken()); + } + + [Fact] + public void SkipForwardTo_PastDocumentEnd() + { + var sourceText = SourceText.From("class C { }"); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + parser.SkipForwardTo(100); + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(100, 0), "", parser.ParseNextToken()); + } + + [Fact] + public void SkipForwardTo_CannotSkipBack() + { + var sourceText = SourceText.From("class C { }"); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + parser.SkipForwardTo(0); + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(0, 6), "class ", parser.ParseNextToken()); + Assert.Throws(() => parser.SkipForwardTo(0)); + } + + [Fact] + public void ResetToPreservedDirectiveContext() + { + var sourceText = SourceText.From(""" + #if true + class C + { + + #else + } + #endif + """.NormalizeLineEndings()); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(0, 16), """ + #if true + class + """, parser.ParseNextToken()); + + SyntaxTokenParser.Result cTokenResult = parser.ParseNextToken(); + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.None, new TextSpan(16, 3), """ + C + + """, cTokenResult); + + verifyAfterC(parser); + + parser.ResetTo(cTokenResult); + + Assert.Equal(cTokenResult, parser.ParseNextToken()); + + verifyAfterC(parser); + + static void verifyAfterC(SyntaxTokenParser parser) + { + AssertToken(expectedKind: SyntaxKind.OpenBraceToken, expectedContextualKind: SyntaxKind.None, new TextSpan(19, 3), """ + { + + """, parser.ParseNextToken()); + + AssertToken(expectedKind: SyntaxKind.EndOfFileToken, expectedContextualKind: SyntaxKind.None, new TextSpan(22, 18), """ + + #else + } + #endif + """, parser.ParseNextToken()); + } + } + + [Fact] + public void ResultContextualKind() + { + var sourceText = SourceText.From("when identifier class"); + var parser = SyntaxFactory.CreateTokenParser(sourceText, TestOptions.Regular); + + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.WhenKeyword, new TextSpan(0, 5), "when ", parser.ParseNextToken()); + AssertToken(expectedKind: SyntaxKind.IdentifierToken, expectedContextualKind: SyntaxKind.None, new TextSpan(5, 11), "identifier ", parser.ParseNextToken()); + AssertToken(expectedKind: SyntaxKind.ClassKeyword, expectedContextualKind: SyntaxKind.None, new TextSpan(16, 5), "class", parser.ParseNextToken()); + } + + private static void AssertToken(SyntaxKind expectedKind, SyntaxKind expectedContextualKind, TextSpan expectedFullSpan, string expectedText, SyntaxTokenParser.Result result) + { + Assert.Equal(expectedKind, result.Token.Kind()); + Assert.Equal(expectedContextualKind, result.ContextualKind); + AssertEx.Equal(expectedText.NormalizeLineEndings(), result.Token.ToFullString()); + Assert.Null(result.Token.Parent); + Assert.Equal(expectedFullSpan, result.Token.FullSpan); + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs index 971eab148dc8e..6a8d436530645 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs @@ -776,17 +776,12 @@ public void GenericAsyncTask_01() foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { UsingDeclaration("async Task' expected // async Task' expected - // async Task").WithLocation(1, 41) + Diagnostic(ErrorCode.ERR_SyntaxError, "Method").WithArguments(">").WithLocation(1, 35) ); - N(SyntaxKind.IncompleteMember); + + N(SyntaxKind.MethodDeclaration); { N(SyntaxKind.AsyncKeyword); N(SyntaxKind.GenericName); @@ -807,14 +802,16 @@ public void GenericAsyncTask_01() N(SyntaxKind.IdentifierToken, "SomeType"); } } - M(SyntaxKind.CommaToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "Method"); - } M(SyntaxKind.GreaterThanToken); } } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); } EOF(); } @@ -827,17 +824,12 @@ public void GenericPublicTask_01() foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) { UsingDeclaration("public Task' expected + // (1,36): error CS1003: Syntax error, '>' expected // public Task").WithLocation(1, 42) + Diagnostic(ErrorCode.ERR_SyntaxError, "Method").WithArguments(">").WithLocation(1, 36) ); - N(SyntaxKind.IncompleteMember); + + N(SyntaxKind.MethodDeclaration); { N(SyntaxKind.PublicKeyword); N(SyntaxKind.GenericName); @@ -858,14 +850,16 @@ public void GenericPublicTask_01() N(SyntaxKind.IdentifierToken, "SomeType"); } } - M(SyntaxKind.CommaToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "Method"); - } M(SyntaxKind.GreaterThanToken); } } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); } EOF(); } @@ -10477,5 +10471,6862 @@ public class Class } EOF(); } + + #region Missing > after generic + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) GetAllValues(Type t) + { + if (!t.IsEnum) + throw new ArgumentException("no good"); + + return Enum.GetValues(t).Cast().Select(e => (e.ToString(), e.ToString())); + } + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) GetAllValues(Type t) + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IfStatement); + { + N(SyntaxKind.IfKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.LogicalNotExpression); + { + N(SyntaxKind.ExclamationToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "IsEnum"); + } + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.ThrowStatement); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.ObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ArgumentException"); + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.StringLiteralExpression); + { + N(SyntaxKind.StringLiteralToken, "\"no good\""); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.ReturnStatement); + { + N(SyntaxKind.ReturnKeyword); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Enum"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "GetValues"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "t"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Cast"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Enum"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Select"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ToString"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ToString"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + // Parser expects: + // static IEnumerable<(string Value, string Description)> A(Type t); + const string source = + """ + static IEnumerable<(string Value, string Description) A' expected + // static IEnumerable<(string Value, string Description) A").WithLocation(1, 55), + // (1,59): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) A' expected + // static IEnumerable<(string Value, string Description) A").WithLocation(1, 71)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "A"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), int GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,60): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), int GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 60)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method04() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), A::X GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,61): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), A::X GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 61)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method05() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), X.Y GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,60): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), X.Y GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 60)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method06() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), A' expected + // static IEnumerable<(string Value, string Description), A").WithLocation(1, 60), + // (1,60): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), A").WithLocation(1, 60)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "A"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + M(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method07() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), A GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,61): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), A GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 61)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "A"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method08() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), ref int GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,64): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), ref int GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 64)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method09() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), int* GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,61): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), int* GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 61)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PointerType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.AsteriskToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method10() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), int[] GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,62): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), int[] GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 62)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method11() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), string[] GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,65): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), string[] GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 65)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method12() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), int*[] GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,63): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), int*[] GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 63)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PointerType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method13() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description), (X[], Y.Z) GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,67): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description), (X[], Y.Z) GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 67)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Z"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method14() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A.GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IEnumerable<(string Value, string Description) A.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IEnumerable<(string Value, string Description) A.GetAllValues").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) A.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "A").WithArguments(",").WithLocation(1, 55), + // (1,69): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 69)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method15() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IEnumerable<(string Value, string Description) A::GetAllValues").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "A").WithArguments(",").WithLocation(1, 55), + // (1,70): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 70)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method16() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IEnumerable<(string Value, string Description) A::B.GetAllValues").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "A").WithArguments(",").WithLocation(1, 55), + // (1,72): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 72)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method17() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) GetAllValues' expected + // static IEnumerable<(string Value, string Description) GetAllValues").WithLocation(1, 55), + // (1,69): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) GetAllValues").WithLocation(1, 69)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method18() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments(">").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method19() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) B.GetAllValues' expected + // static IEnumerable<(string Value, string Description) B.GetAllValues").WithLocation(1, 71), + // (1,71): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) B.GetAllValues").WithLocation(1, 71)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method20() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) B.GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IEnumerable<(string Value, string Description) B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IEnumerable<(string Value, string Description) B.GetAllValues").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "B").WithArguments(",").WithLocation(1, 55), + // (1,72): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 72)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method21() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A::B.GetAllValues' expected + // static IEnumerable<(string Value, string Description) A::B.GetAllValues").WithLocation(1, 74), + // (1,74): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A::B.GetAllValues").WithLocation(1, 74)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method22() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IEnumerable<(string Value, string Description) A::B.GetAllValues").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "A").WithArguments(",").WithLocation(1, 55), + // (1,75): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A::B.GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 75)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method23() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A::GetAllValues' expected + // static IEnumerable<(string Value, string Description) A::GetAllValues").WithLocation(1, 72), + // (1,72): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A::GetAllValues").WithLocation(1, 72)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Method24() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IEnumerable<(string Value, string Description) A::GetAllValues").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, ',' expected + // static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "A").WithArguments(",").WithLocation(1, 55), + // (1,73): error CS1003: Syntax error, '>' expected + // static IEnumerable<(string Value, string Description) A::GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 73)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) Type> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) Type> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "Type").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) int> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) int> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) Alias::X> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) Alias::X> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "Alias").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Alias"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method04() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) Outer.Inner> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) Outer.Inner> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "Outer").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Outer"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Inner"); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method05() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + // Unfortunately in this case we expect: + // static IDictionary<(string Value, string Description)> IEnumerable<@T>(GetAllValues @p1, (Type t) @p2); + const string source = + """ + static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, '>' expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "IEnumerable").WithArguments(">").WithLocation(1, 55), + // (1,67): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "string").WithLocation(1, 67), + // (1,67): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "string").WithArguments(",").WithLocation(1, 67), + // (1,75): error CS1003: Syntax error, '(' expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "GetAllValues").WithArguments("(").WithLocation(1, 75), + // (1,87): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 87), + // (1,87): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(",").WithLocation(1, 87), + // (1,94): error CS8124: Tuple must contain at least two elements. + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(1, 94), + // (1,95): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 95), + // (1,95): error CS1026: ) expected + // static IDictionary<(string Value, string Description) IEnumerable GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 95)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.TypeParameter); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method06() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + // Unfortunately in this case we expect: + // static IDictionary<(string Value, string Description)> IEnumerable<@T>(GetAllValues @p1, (Type t) @p2); + const string source = + """ + static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, '>' expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "IEnumerable").WithArguments(">").WithLocation(1, 55), + // (1,67): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "string").WithLocation(1, 67), + // (1,67): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "string").WithArguments(",").WithLocation(1, 67), + // (1,74): error CS1003: Syntax error, '(' expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, ">").WithArguments("(").WithLocation(1, 74), + // (1,74): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ">").WithLocation(1, 74), + // (1,88): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(1, 88), + // (1,88): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(",").WithLocation(1, 88), + // (1,95): error CS8124: Tuple must contain at least two elements. + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(1, 95), + // (1,96): error CS1001: Identifier expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 96), + // (1,96): error CS1026: ) expected + // static IDictionary<(string Value, string Description) IEnumerable> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 96)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + M(SyntaxKind.TypeParameter); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + M(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "GetAllValues"); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method07() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) (string, int)> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IDictionary<(string Value, string Description) (string, int)> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IDictionary<(string Value, string Description) ").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, '>' expected + // static IDictionary<(string Value, string Description) (string, int)> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 55)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method08() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) (X, X.X, X.X.X)> GetAllValues GetAllValues' expected + // static IDictionary<(string Value, string Description) (X, X.X, X.X.X)> GetAllValues").WithLocation(1, 55)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method09() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) (A GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IDictionary<(string Value, string Description) (A GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IDictionary<(string Value, string Description) ").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, '>' expected + // static IDictionary<(string Value, string Description) (A GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 55)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method10() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) (A, C.D)> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,1): error CS1073: Unexpected token '(' + // static IDictionary<(string Value, string Description) (A, C.D)> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "static IDictionary<(string Value, string Description) ").WithArguments("(").WithLocation(1, 1), + // (1,55): error CS1003: Syntax error, '>' expected + // static IDictionary<(string Value, string Description) (A, C.D)> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 55)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method11() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) int*> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) int*> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.PointerType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method12() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) void*> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) void*> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "void").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.PointerType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method13() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) String**> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) String**> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "String").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.PointerType); + { + N(SyntaxKind.PointerType); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "String"); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method14() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) int[]> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) int[]> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method15() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static IDictionary<(string Value, string Description) int*[]> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) int*[]> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 55)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PointerType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingCommaIncludingAngleBracket_Method16() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + // We actually parse + // static IDictionary<(string Value, string Description), int> GetAllValues(Type t); + // and entirely ignore the ref, but provide the ',' expected error in both places + const string source = + """ + static IDictionary<(string Value, string Description) ref int> GetAllValues(Type t); + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) ref int> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "ref").WithArguments(",").WithLocation(1, 55), + // (1,59): error CS1003: Syntax error, ',' expected + // static IDictionary<(string Value, string Description) ref int> GetAllValues(Type t); + Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 59)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "GetAllValues"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method' expected + // static void Method").WithLocation(1, 21), + // (3,14): error CS1003: Syntax error, '>' expected + // Action").WithLocation(3, 14)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + 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, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "t"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method' expected + // static void Method").WithLocation(1, 21), + // (3,29): error CS1003: Syntax error, '>' expected + // Action").WithLocation(3, 29), + // (3,29): error CS1003: Syntax error, '>' expected + // Action").WithLocation(3, 29)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + 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, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "t"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method)Method, (Action)Method' expected + // static void Method").WithLocation(1, 21)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "u"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment04() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method)Method, (Action>)Method' expected + // static void Method").WithLocation(1, 21)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "u"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment05() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method' expected + // static void Method").WithLocation(1, 24), + // (3,17): error CS1003: Syntax error, '>' expected + // Action").WithLocation(3, 17)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "U"); + } + M(SyntaxKind.GreaterThanToken); + } + 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, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "t"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "U"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment06() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method' expected + // static void Method").WithLocation(1, 24), + // (3,32): error CS1003: Syntax error, '>' expected + // Action").WithLocation(3, 32), + // (3,32): error CS1003: Syntax error, '>' expected + // Action").WithLocation(3, 32)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "U"); + } + M(SyntaxKind.GreaterThanToken); + } + 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, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + M(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "t"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "U"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment07() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method)Method, (Action)Method' expected + // static void Method").WithLocation(1, 24)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "U"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "u"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_GenericDelegateAssignment08() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + static void Method)Method, (Action>)Method' expected + // static void Method").WithLocation(1, 24)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "U"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.DeclarationExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.ParenthesizedVariableDesignation); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SingleVariableDesignation); + { + N(SyntaxKind.IdentifierToken, "u"); + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.TupleExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Method"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.CastExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Method"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "U"); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Property01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) Values { get; set; } + """; + + UsingDeclaration(source, options, + // (1,48): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) Values { get; set; } + Diagnostic(ErrorCode.ERR_SyntaxError, "Values").WithArguments(">").WithLocation(1, 48)); + + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Values"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Property02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) Values => null; + """; + + UsingDeclaration(source, options, + // (1,48): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) Values => null; + Diagnostic(ErrorCode.ERR_SyntaxError, "Values").WithArguments(">").WithLocation(1, 48)); + + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Values"); + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Property03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + Dictionary' expected + // Dictionary").WithLocation(1, 24)); + + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Dictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Values"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Property04() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description Values { get; set; } + """; + + UsingDeclaration(source, options, + // (1,47): error CS1026: ) expected + // IEnumerable<(string Value, string Description Values { get; set; } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "Values").WithLocation(1, 47), + // (1,47): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description Values { get; set; } + Diagnostic(ErrorCode.ERR_SyntaxError, "Values").WithArguments(">").WithLocation(1, 47)); + + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Values"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Property05() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description Values => null; + """; + + UsingDeclaration(source, options, + // (1,47): error CS1026: ) expected + // IEnumerable<(string Value, string Description Values => null; + Diagnostic(ErrorCode.ERR_CloseParenExpected, "Values").WithLocation(1, 47), + // (1,47): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description Values => null; + Diagnostic(ErrorCode.ERR_SyntaxError, "Values").WithArguments(">").WithLocation(1, 47)); + + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + M(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Values"); + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Local01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) values; + """; + + UsingStatement(source, options, + // (1,48): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) values; + Diagnostic(ErrorCode.ERR_SyntaxError, "values").WithArguments(">").WithLocation(1, 48)); + + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "values"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Local02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) values = null; + """; + + UsingStatement(source, options, + // (1,48): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) values = null; + Diagnostic(ErrorCode.ERR_SyntaxError, "values").WithArguments(">").WithLocation(1, 48)); + + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "values"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Local03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) + values = null, + otherValues, + moreValues + ; + """; + + UsingStatement(source, options, + // (1,47): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(">").WithLocation(1, 47)); + + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "values"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "otherValues"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "moreValues"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Field01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) values; + """; + + UsingDeclaration(source, options, + // (1,48): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) values; + Diagnostic(ErrorCode.ERR_SyntaxError, "values").WithArguments(">").WithLocation(1, 48) + ); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "values"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Field02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) values = null; + """; + + UsingDeclaration(source, options, + // (1,48): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) values = null; + Diagnostic(ErrorCode.ERR_SyntaxError, "values").WithArguments(">").WithLocation(1, 48)); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "values"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(24642, "https://github.com/dotnet/roslyn/issues/24642")] + public void MissingClosingAngleBracket_Field03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + IEnumerable<(string Value, string Description) + values = null, + otherValues, + moreValues + ; + """; + + UsingDeclaration(source, options, + // (1,47): error CS1003: Syntax error, '>' expected + // IEnumerable<(string Value, string Description) + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments(">").WithLocation(1, 47)); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "values"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "otherValues"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "moreValues"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodArgumentList01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public void M(ImmutableArray' expected + // public void M(ImmutableArray").WithLocation(1, 34)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "arr"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodArgumentList02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public void M(ImmutableArray another); + """; + + UsingDeclaration(source, options, + // (1,34): error CS1003: Syntax error, ',' expected + // public void M(ImmutableArray another); + Diagnostic(ErrorCode.ERR_SyntaxError, "arr").WithArguments(",").WithLocation(1, 34), + // (1,59): error CS1003: Syntax error, '>' expected + // public void M(ImmutableArray another); + Diagnostic(ErrorCode.ERR_SyntaxError, "another").WithArguments(">").WithLocation(1, 59)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "arr"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "another"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodArgumentList03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public ImmutableArray' expected + // public ImmutableArray").WithLocation(1, 27), + // (1,48): error CS1003: Syntax error, ',' expected + // public ImmutableArray' expected + // public ImmutableArray").WithLocation(1, 70), + // (1,70): error CS1003: Syntax error, '>' expected + // public ImmutableArray").WithLocation(1, 70)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodArgumentList04() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public ImmutableArray' expected + // public ImmutableArray").WithLocation(1, 25), + // (1,28): error CS1003: Syntax error, '>' expected + // public ImmutableArray").WithLocation(1, 28), + // (1,46): error CS1003: Syntax error, ',' expected + // public ImmutableArray' expected + // public ImmutableArray").WithLocation(1, 66), + // (1,66): error CS1003: Syntax error, '>' expected + // public ImmutableArray").WithLocation(1, 66)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodInvocation01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + Invoke' expected + // Invoke").WithLocation(1, 43), + // (1,74): error CS1003: Syntax error, '>' expected + // Invoke").WithLocation(1, 74)); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Invoke"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "31"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DefaultExpression); + { + N(SyntaxKind.DefaultKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodInvocation02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + Invoke(31, default(ImmutableArray' expected + // Invoke(31, default(ImmutableArray").WithLocation(1, 75)); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Invoke"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "31"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DefaultExpression); + { + N(SyntaxKind.DefaultKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_MethodInvocation03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + Invoke(31, default(ImmutableArray)); + """; + + UsingStatement(source, options); + + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Invoke"); + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "31"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.DefaultExpression); + { + N(SyntaxKind.DefaultKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_RecordPrimaryConstructorArgumentList01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public record M(ImmutableArray' expected + // public record M(ImmutableArray").WithLocation(1, 36)); + + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Array"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_RecordPrimaryConstructorArgumentList02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public record M' expected + // public record M").WithLocation(1, 18), + // (1,36): error CS1003: Syntax error, '>' expected + // public record M").WithLocation(1, 36)); + + N(SyntaxKind.RecordDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.RecordKeyword); + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "ImmutableArray"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "Array"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_RecordPrimaryConstructorArgumentList03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public record M' expected + // public record M").WithLocation(1, 18), + // (1,36): error CS1003: Syntax error, '>' expected + // public record M").WithLocation(1, 36), + // (2,14): error CS1003: Syntax error, '>' expected + // : Other").WithLocation(2, 14), + // (2,33): error CS1003: Syntax error, ',' expected + // : Other' expected + // public IEnumerable<(string Value, string Description) this[string index] { get; } + Diagnostic(ErrorCode.ERR_SyntaxError, "this").WithArguments(">").WithLocation(1, 55)); + + N(SyntaxKind.IndexerDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.BracketedParameterList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "index"); + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_ThisAccessor02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public IEnumerable<(string Value, string Description) this[IDictionary' expected + // public IEnumerable<(string Value, string Description) this[IDictionary").WithLocation(1, 55), + // (1,84): error CS1003: Syntax error, '>' expected + // public IEnumerable<(string Value, string Description) this[IDictionary").WithLocation(1, 84)); + + N(SyntaxKind.IndexerDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.BracketedParameterList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "keys"); + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_ThisAccessor03() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public IEnumerable<(string Value, string Description) this[IDictionary null; + """; + + UsingDeclaration(source, options, + // (1,55): error CS1003: Syntax error, '>' expected + // public IEnumerable<(string Value, string Description) this[IDictionary null; + Diagnostic(ErrorCode.ERR_SyntaxError, "this").WithArguments(">").WithLocation(1, 55), + // (1,84): error CS1003: Syntax error, '>' expected + // public IEnumerable<(string Value, string Description) this[IDictionary null; + Diagnostic(ErrorCode.ERR_SyntaxError, "keys").WithArguments(">").WithLocation(1, 84)); + + N(SyntaxKind.IndexerDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.BracketedParameterList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IDictionary"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.IdentifierToken, "keys"); + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_Operator01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public static IEnumerable<(string Value, string Description) operator +(X left, X right); + """; + + UsingDeclaration(source, options, + // (1,62): error CS1003: Syntax error, '>' expected + // public static IEnumerable<(string Value, string Description) operator +(X left, X right); + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(">").WithLocation(1, 62)); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.IdentifierToken, "left"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.IdentifierToken, "right"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_Operator02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public static IEnumerable<(string Value, string Description) operator checked +(X left, X right); + """; + + UsingDeclaration(source, options, + // (1,62): error CS1003: Syntax error, '>' expected + // public static IEnumerable<(string Value, string Description) operator checked +(X left, X right); + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(">").WithLocation(1, 62)); + + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.CheckedKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.IdentifierToken, "left"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.IdentifierToken, "right"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_ConversionOperator01() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public static implicit operator IEnumerable<(string Value, string Description)(X source); + """; + + UsingDeclaration(source, options, + // (1,79): error CS1003: Syntax error, '>' expected + // public static implicit operator IEnumerable<(string Value, string Description)(X source); + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments(">").WithLocation(1, 79)); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Value"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Description"); + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.IdentifierToken, "source"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + [Fact] + [WorkItem(48566, "https://github.com/dotnet/roslyn/issues/48566")] + public void MissingClosingAngleBracket_ConversionOperator02() + { + foreach (var options in new[] { TestOptions.Script, TestOptions.Regular }) + { + const string source = + """ + public static implicit operator IEnumerable' expected + // public static implicit operator IEnumerable").WithLocation(1, 51)); + + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "IEnumerable"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + M(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.IdentifierToken, "source"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + } + + #endregion } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs index 51fd79f19312e..ddc8174b9dbe3 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests2.cs @@ -945,5 +945,421 @@ public void ExtendedPropertySubpattern_InPositionalPattern() } EOF(); } + + #region Missing > in type parameter list + + [Fact] + public void MissingClosingAngleBracket01() + { + UsingExpression(@"e is List' expected + // e is List").WithLocation(1, 15), + // (1,30): error CS1525: Invalid expression term 'int' + // e is List' expected + // e is not List").WithLocation(1, 19), + // (1,39): error CS1525: Invalid expression term 'int' + // e is not List' expected + // e is (not List").WithLocation(1, 20), + // (1,40): error CS1525: Invalid expression term 'int' + // e is (not List' expected + // e is (not List").WithLocation(1, 57), + // (1,77): error CS1525: Invalid expression term 'int' + // e is (not List' expected + // e is A.B").WithLocation(1, 12)); + + N(SyntaxKind.LessThanExpression); + { + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.TypePattern); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "B"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + M(SyntaxKind.GreaterThanToken); + } + } + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "D"); + } + } + } + } + } + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Y"); + } + } + EOF(); + } + + #endregion } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs index 17d12a4d447e1..18a55b0dd7804 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs @@ -9236,38 +9236,34 @@ public void GlobalStatementSeparators_Comma2() a < b, void goo() { } ", - // (2,6): error CS1002: ; expected - // a < b, - Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(2, 6), - // (2,6): error CS7017: Member definition, statement, or end-of-file expected - // a < b, - Diagnostic(ErrorCode.ERR_GlobalDefinitionOrStatementExpected, ",").WithLocation(2, 6)); + // (3,1): error CS1547: Keyword 'void' cannot be used in this context + // void goo() { } + Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(3, 1), + // (3,6): error CS1003: Syntax error, '>' expected + // void goo() { } + Diagnostic(ErrorCode.ERR_SyntaxError, "goo").WithArguments(">").WithLocation(3, 6)); + N(SyntaxKind.CompilationUnit); { - N(SyntaxKind.GlobalStatement); + N(SyntaxKind.MethodDeclaration); { - N(SyntaxKind.ExpressionStatement); + N(SyntaxKind.GenericName); { - N(SyntaxKind.LessThanExpression); + N(SyntaxKind.IdentifierToken, "a"); + N(SyntaxKind.TypeArgumentList); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "a"); - } N(SyntaxKind.LessThanToken); N(SyntaxKind.IdentifierName); { N(SyntaxKind.IdentifierToken, "b"); } + N(SyntaxKind.CommaToken); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + M(SyntaxKind.GreaterThanToken); } - M(SyntaxKind.SemicolonToken); - } - } - N(SyntaxKind.MethodDeclaration); - { - N(SyntaxKind.PredefinedType); - { - N(SyntaxKind.VoidKeyword); } N(SyntaxKind.IdentifierToken, "goo"); N(SyntaxKind.ParameterList); diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs index f8f0d20bfe449..b9d54c3d01f96 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs @@ -5,6 +5,9 @@ #nullable disable using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; @@ -276,5 +279,68 @@ public void IsAttributeTargetSpecifier() } } } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72300")] + public void TestAllKindsReturnedFromGetKindsMethodsExist() + { + foreach (var method in typeof(SyntaxFacts).GetMethods(BindingFlags.Public | BindingFlags.Static)) + { + if (method.ReturnType == typeof(IEnumerable) && method.GetParameters() is []) + { + foreach (var kind in (IEnumerable)method.Invoke(null, null)) + { + Assert.True(Enum.IsDefined(typeof(SyntaxKind), kind), $"Nonexistent kind '{kind}' returned from method '{method.Name}'"); + } + } + } + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72300")] + [InlineData(nameof(SyntaxFacts.GetContextualKeywordKinds), SyntaxKind.YieldKeyword, SyntaxKind.ElifKeyword)] + [InlineData(nameof(SyntaxFacts.GetPunctuationKinds), SyntaxKind.TildeToken, SyntaxKind.BoolKeyword)] + [InlineData(nameof(SyntaxFacts.GetReservedKeywordKinds), SyntaxKind.BoolKeyword, SyntaxKind.YieldKeyword)] + public void TestRangeBasedGetKindsMethodsReturnExpectedResults(string methodName, SyntaxKind lowerBoundInclusive, SyntaxKind upperBoundExclusive) + { + var method = typeof(SyntaxFacts).GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); + + Assert.NotNull(method); + Assert.Equal(0, method.GetParameters().Length); + Assert.Equal(typeof(IEnumerable), method.ReturnType); + + var returnedKindsInts = ((IEnumerable)method.Invoke(null, null)).Select(static k => (int)k).ToHashSet(); + + for (int i = (int)lowerBoundInclusive; i < (int)upperBoundExclusive; i++) + { + if (Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i)) + { + Assert.True(returnedKindsInts.Remove(i)); + } + else + { + Assert.DoesNotContain(i, returnedKindsInts); + } + } + + // We've already removed all expected kinds from the set. It should be empty now + Assert.Empty(returnedKindsInts); + } + + [Fact] + public void TestGetPreprocessorKeywordKindsReturnsExpectedResults() + { + var returnedKindsInts = SyntaxFacts.GetPreprocessorKeywordKinds().Select(static k => (int)k).ToHashSet(); + + Assert.True(returnedKindsInts.Remove((int)SyntaxKind.TrueKeyword)); + Assert.True(returnedKindsInts.Remove((int)SyntaxKind.FalseKeyword)); + Assert.True(returnedKindsInts.Remove((int)SyntaxKind.DefaultKeyword)); + + for (int i = (int)SyntaxKind.ElifKeyword; i < (int)SyntaxKind.ReferenceKeyword; i++) + { + Assert.True(returnedKindsInts.Remove(i)); + } + + // We've already removed all expected kinds from the set. It should be empty now + Assert.Empty(returnedKindsInts); + } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/ArrayBuilderTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/ArrayBuilderTests.cs index 29a4983f3362d..79716c5880351 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/ArrayBuilderTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/ArrayBuilderTests.cs @@ -13,6 +13,30 @@ namespace Microsoft.CodeAnalysis.UnitTests.Collections { public class ArrayBuilderTests { + [Fact] + public void RemoveAll() + { + var builder = new ArrayBuilder { 6, 5, 1, 2, 3, 2, 4, 5, 1, 7 }; + + builder.RemoveAll((_, _) => false, arg: 0); + AssertEx.Equal([6, 5, 1, 2, 3, 2, 4, 5, 1, 7], builder); + + builder.RemoveAll((i, arg) => i == arg, arg: 6); + AssertEx.Equal([5, 1, 2, 3, 2, 4, 5, 1, 7], builder); + + builder.RemoveAll((i, arg) => i == arg, arg: 7); + AssertEx.Equal([5, 1, 2, 3, 2, 4, 5, 1], builder); + + builder.RemoveAll((i, arg) => i < arg, arg: 3); + AssertEx.Equal([5, 3, 4, 5], builder); + + builder.RemoveAll((_, _) => true, arg: 0); + AssertEx.Equal([], builder); + + builder.RemoveAll((_, _) => true, arg: 0); + AssertEx.Equal([], builder); + } + [Fact] public void RemoveDuplicates1() { diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableArrayExtensionsTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableArrayExtensionsTests.cs index 994564ca25cae..537dd761567aa 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableArrayExtensionsTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/ImmutableArrayExtensionsTests.cs @@ -147,13 +147,26 @@ public void Single() Func isOdd = x => x % 2 == 1; - // BUG:753260 Should this be ArgumentNullException for consistency? Assert.Throws(() => default(ImmutableArray).Single(isOdd)); Assert.Throws(() => ImmutableArray.Create().Single(isOdd)); Assert.Equal(1, ImmutableArray.Create(1, 2).Single(isOdd)); Assert.Throws(() => ImmutableArray.Create(1, 2, 3).Single(isOdd)); } + [Fact] + public void Single_Arg() + { + Assert.Throws(() => default(ImmutableArray).Single((_, _) => true, 1)); + Assert.Throws(() => ImmutableArray.Create().Single((x, a) => x == a, 1)); + Assert.Equal(1, ImmutableArray.Create(1).Single((x, a) => x == a, 1)); + Assert.Throws(() => ImmutableArray.Create(1, 1).Single((x, a) => x == a, 1)); + + Assert.Throws(() => default(ImmutableArray).Single((x, a) => x % a == 1, 2)); + Assert.Throws(() => ImmutableArray.Create().Single((x, a) => x % a == 1, 2)); + Assert.Equal(1, ImmutableArray.Create(1, 2).Single((x, a) => x % a == 1, 2)); + Assert.Throws(() => ImmutableArray.Create(1, 2, 3).Single((x, a) => x % a == 1, 2)); + } + [Fact] public void IndexOf() { diff --git a/src/Compilers/Core/CodeAnalysisTest/CorLibTypesTests.cs b/src/Compilers/Core/CodeAnalysisTest/CorLibTypesTests.cs index 98714bf0588b6..a2822f4fb80de 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CorLibTypesTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/CorLibTypesTests.cs @@ -17,10 +17,10 @@ public class CorLibTypesAndConstantTests : TestBase [Fact] public void IntegrityTest() { - for (int i = 1; i <= (int)SpecialType.Count; i++) + for (int i = 1; i < (int)InternalSpecialType.NextAvailable; i++) { - string name = SpecialTypes.GetMetadataName((SpecialType)i); - Assert.Equal((SpecialType)i, SpecialTypes.GetTypeFromMetadataName(name)); + string name = SpecialTypes.GetMetadataName((ExtendedSpecialType)i); + Assert.Equal((ExtendedSpecialType)i, SpecialTypes.GetTypeFromMetadataName(name)); } for (int i = 0; i <= (int)SpecialType.Count; i++) diff --git a/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs b/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs new file mode 100644 index 0000000000000..4689c2b19fc62 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/Symbols/SpecialTypeTests.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests +{ + public class SpecialTypeTests + { + [Fact] + public void ExtendedSpecialType_ToString() + { + AssertEx.Equal("0", ((ExtendedSpecialType)SpecialType.None).ToString()); + AssertEx.Equal("System_Object", ((ExtendedSpecialType)1).ToString()); + AssertEx.Equal("System_Runtime_CompilerServices_InlineArrayAttribute", ((ExtendedSpecialType)SpecialType.Count).ToString()); + AssertEx.Equal("System_ReadOnlySpan_T", ((ExtendedSpecialType)InternalSpecialType.First).ToString()); + AssertEx.Equal("System_ReadOnlySpan_T", ((ExtendedSpecialType)InternalSpecialType.System_ReadOnlySpan_T).ToString()); + AssertEx.Equal("System_Reflection_MethodInfo", ((ExtendedSpecialType)(InternalSpecialType.NextAvailable - 1)).ToString()); + AssertEx.Equal("52", ((ExtendedSpecialType)InternalSpecialType.NextAvailable).ToString()); + } + } +} diff --git a/src/Compilers/Core/CommandLine/BuildProtocol.cs b/src/Compilers/Core/CommandLine/BuildProtocol.cs index b2fa1ec0b622a..e315beaf65a28 100644 --- a/src/Compilers/Core/CommandLine/BuildProtocol.cs +++ b/src/Compilers/Core/CommandLine/BuildProtocol.cs @@ -132,16 +132,8 @@ public static async Task ReadAsync(Stream inStream, CancellationTo throw new ArgumentException($"Request is over {MaximumRequestSize >> 20}MB in length"); } - cancellationToken.ThrowIfCancellationRequested(); - - // Read the full request - var requestBuffer = new byte[length]; - await ReadAllAsync(inStream, requestBuffer, length, cancellationToken).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - // Parse the request into the Request data structure. - using var reader = new BinaryReader(new MemoryStream(requestBuffer), Encoding.Unicode); + using var reader = new BinaryReader(inStream, Encoding.Unicode, leaveOpen: true); var requestId = reader.ReadString(); var language = (RequestLanguage)reader.ReadUInt32(); var compilerHash = reader.ReadString(); diff --git a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index b2a9dfe48325f..68ceaeecff859 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -753,7 +753,7 @@ private bool ValidateBootstrapResponse(BuildResponse? response) Log.LogError($"Critical error {responseType} when building"); return false; case BuildResponse.ResponseType.Rejected: - Log.LogError($"Compiler request rejected"); + Log.LogError($"Compiler request rejected: {((RejectedBuildResponse)response!).Reason}"); return false; case BuildResponse.ResponseType.CannotConnect: if (Interlocked.Increment(ref s_connectFailedCount) > maxCannotConnectCount) diff --git a/src/Compilers/Core/Portable/CodeGen/ItemTokenMap.cs b/src/Compilers/Core/Portable/CodeGen/ItemTokenMap.cs index f0cdc1483e780..7d387f9def7ea 100644 --- a/src/Compilers/Core/Portable/CodeGen/ItemTokenMap.cs +++ b/src/Compilers/Core/Portable/CodeGen/ItemTokenMap.cs @@ -62,7 +62,7 @@ public T GetItem(uint token) } } - public IEnumerable GetAllItems() + public T[] CopyItems() { lock (_items) { diff --git a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs index f837871a5f7dc..51ccab0fd9398 100644 --- a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs @@ -636,6 +636,32 @@ public static async Task AnyAsync(this ImmutableArray array, F return default; } + public static TValue? Single(this ImmutableArray array, Func predicate, TArg arg) + { + var hasValue = false; + TValue? value = default; + foreach (var item in array) + { + if (predicate(item, arg)) + { + if (hasValue) + { + throw ExceptionUtilities.Unreachable(); + } + + value = item; + hasValue = true; + } + } + + if (!hasValue) + { + throw ExceptionUtilities.Unreachable(); + } + + return value; + } + /// /// Casts the immutable array of a Type to an immutable array of its base type. /// diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 9d5d592c2f0e4..e5af04c78c4d1 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -955,6 +955,11 @@ public Compilation ReplaceReference(MetadataReference oldReference, MetadataRefe /// public INamedTypeSymbol GetSpecialType(SpecialType specialType) { + if (specialType <= SpecialType.None || specialType > SpecialType.Count) + { + throw new ArgumentOutOfRangeException(nameof(specialType), $"Unexpected SpecialType: '{(int)specialType}'."); + } + return (INamedTypeSymbol)CommonGetSpecialType(specialType).GetITypeSymbol(); } @@ -3688,7 +3693,7 @@ private bool IsMemberMissing(int member) return _lazyMakeMemberMissingMap != null && _lazyMakeMemberMissingMap.ContainsKey(member); } - internal void MakeTypeMissing(SpecialType type) + internal void MakeTypeMissing(ExtendedSpecialType type) { MakeTypeMissing((int)type); } @@ -3708,7 +3713,7 @@ private void MakeTypeMissing(int type) _lazyMakeWellKnownTypeMissingMap[(int)type] = true; } - internal bool IsTypeMissing(SpecialType type) + internal bool IsTypeMissing(ExtendedSpecialType type) { return IsTypeMissing((int)type); } diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index 1bc7079e2a2d8..cd10a5c8a8eca 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -368,11 +368,11 @@ public Cci.IAssemblyReference GetContainingAssembly(EmitContext context) } /// - /// Returns User Strings referenced from the IL in the module. + /// Returns copy of User Strings referenced from the IL in the module. /// - public IEnumerable GetStrings() + public string[] CopyStrings() { - return _stringsInILMap.GetAllItems(); + return _stringsInILMap.CopyItems(); } public uint GetFakeSymbolTokenForIL(Cci.IReference symbol, SyntaxNode syntaxNode, DiagnosticBag diagnostics) diff --git a/src/Compilers/Core/Portable/ExtendedSpecialType.cs b/src/Compilers/Core/Portable/ExtendedSpecialType.cs new file mode 100644 index 0000000000000..a5b5eb8212982 --- /dev/null +++ b/src/Compilers/Core/Portable/ExtendedSpecialType.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Microsoft.CodeAnalysis +{ + /// + /// A structure meant to represent a union of and values + /// + [StructLayout(LayoutKind.Explicit, Size = 1)] + internal readonly struct ExtendedSpecialType + { + [FieldOffset(0)] + private readonly sbyte _value; + + private ExtendedSpecialType(int value) + { + Debug.Assert(value >= sbyte.MinValue && value <= sbyte.MaxValue); + _value = (sbyte)value; + } + + public static implicit operator ExtendedSpecialType(SpecialType value) => new ExtendedSpecialType((int)value); + public static explicit operator SpecialType(ExtendedSpecialType value) => value._value < (int)InternalSpecialType.First ? (SpecialType)value._value : SpecialType.None; + + public static implicit operator ExtendedSpecialType(InternalSpecialType value) => new ExtendedSpecialType((int)value); + + public static explicit operator ExtendedSpecialType(int value) => new ExtendedSpecialType(value); + public static explicit operator int(ExtendedSpecialType value) => value._value; + + public static bool operator ==(ExtendedSpecialType left, ExtendedSpecialType right) => left._value == right._value; + public static bool operator !=(ExtendedSpecialType left, ExtendedSpecialType right) => !(left == right); + + public override bool Equals(object? obj) + { + switch (obj) + { + case ExtendedSpecialType other: + return this == other; + + case SpecialType other: + return this == other; + + case InternalSpecialType other: + return this == other; + + default: + return false; + } + } + + public override int GetHashCode() + { + return _value.GetHashCode(); + } + + public override string ToString() + { + if (_value > (int)SpecialType.None && _value <= (int)SpecialType.Count) + { + return ((SpecialType)_value).ToString(); + } + + if (_value >= (int)InternalSpecialType.First && _value < (int)InternalSpecialType.NextAvailable) + { + return ((InternalSpecialType)_value).ToString(); + } + + return _value.ToString(); + } + } +} diff --git a/src/Compilers/Core/Portable/InternalSpecialType.cs b/src/Compilers/Core/Portable/InternalSpecialType.cs new file mode 100644 index 0000000000000..23030db82f4ae --- /dev/null +++ b/src/Compilers/Core/Portable/InternalSpecialType.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.CodeAnalysis +{ + /// + /// Specifies the Ids of types that are expected to be defined in core library. + /// Unlike ids in enum, these ids are not meant for public consumption + /// and are meant for internal usage in compilers. + /// + internal enum InternalSpecialType : sbyte + { + // Value 0 represents an unknown type + Unknown = SpecialType.None, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library, e.g. from `System.Memory` package. + /// The should be used for that purpose instead + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library + /// + System_ReadOnlySpan_T = SpecialType.Count + 1, + First = System_ReadOnlySpan_T, + + System_IFormatProvider, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library + /// + System_Type, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library + /// + System_Reflection_MethodBase, + + /// + /// Indicates that the type is from the COR library. + /// + /// + /// Check for this special type cannot be used to find the "canonical" definition of + /// since it is fully legal for it to come from sources other than the COR library. + /// The should be used for that purpose instead + /// This entry mostly exists so that compiler can tell this type apart when resolving other members of the COR library + /// + System_Reflection_MethodInfo, + + /// + /// This item should be kept last and it doesn't represent any specific type. + /// + NextAvailable + } +} diff --git a/src/Compilers/Core/Portable/MemberDescriptor.cs b/src/Compilers/Core/Portable/MemberDescriptor.cs index 4428e1143ea9d..b9d932f072818 100644 --- a/src/Compilers/Core/Portable/MemberDescriptor.cs +++ b/src/Compilers/Core/Portable/MemberDescriptor.cs @@ -5,6 +5,7 @@ using Roslyn.Utilities; using System; using System.Collections.Immutable; +using System.Diagnostics; using System.IO; using System.Reflection.Metadata; @@ -44,15 +45,35 @@ internal readonly struct MemberDescriptor /// (either for the VB runtime classes, or types like System.Task etc.) will need /// to use IDs that are all mutually disjoint. /// - public readonly short DeclaringTypeId; + private readonly short _declaringTypeId; + + public bool IsSpecialTypeMember => _declaringTypeId < (int)InternalSpecialType.NextAvailable; + + public ExtendedSpecialType DeclaringSpecialType + { + get + { + Debug.Assert(_declaringTypeId < (int)InternalSpecialType.NextAvailable); + return (ExtendedSpecialType)_declaringTypeId; + } + } + + public WellKnownType DeclaringWellKnownType + { + get + { + Debug.Assert(_declaringTypeId >= (int)WellKnownType.First); + return (WellKnownType)_declaringTypeId; + } + } public string DeclaringTypeMetadataName { get { - return DeclaringTypeId <= (int)SpecialType.Count - ? ((SpecialType)DeclaringTypeId).GetMetadataName()! - : ((WellKnownType)DeclaringTypeId).GetMetadataName(); + return IsSpecialTypeMember + ? DeclaringSpecialType.GetMetadataName()! + : DeclaringWellKnownType.GetMetadataName(); } } @@ -101,7 +122,7 @@ public MemberDescriptor( ushort Arity = 0) { this.Flags = Flags; - this.DeclaringTypeId = DeclaringTypeId; + this._declaringTypeId = DeclaringTypeId; this.Name = Name; this.Arity = Arity; this.Signature = Signature; diff --git a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs index 36ead9791da22..d9dcd6529bed3 100644 --- a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs @@ -9,6 +9,7 @@ using System.Reflection.Metadata.Ecma335; using System.Threading; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Emit; using Roslyn.Utilities; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; @@ -28,7 +29,7 @@ internal sealed class FullMetadataWriter : MetadataWriter private readonly Dictionary _fieldDefIndex; private readonly Dictionary _methodDefIndex; - private readonly Dictionary _parameterListIndex; + private readonly SegmentedDictionary _parameterListIndex; private readonly HeapOrReferenceIndex _assemblyRefIndex; private readonly HeapOrReferenceIndex _moduleRefIndex; @@ -101,7 +102,7 @@ private FullMetadataWriter( _fieldDefIndex = new Dictionary(numTypeDefsGuess, ReferenceEqualityComparer.Instance); _methodDefIndex = new Dictionary(numTypeDefsGuess, ReferenceEqualityComparer.Instance); - _parameterListIndex = new Dictionary(numMethods, ReferenceEqualityComparer.Instance); + _parameterListIndex = new SegmentedDictionary(numMethods, ReferenceEqualityComparer.Instance); _assemblyRefIndex = new HeapOrReferenceIndex(this); _moduleRefIndex = new HeapOrReferenceIndex(this); @@ -430,13 +431,13 @@ private void CreateIndicesFor(IMethodDefinition methodDef) private readonly struct DefinitionIndex where T : class, IReference { // IReference to RowId - private readonly Dictionary _index; - private readonly List _rows; + private readonly SegmentedDictionary _index; + private readonly SegmentedList _rows; public DefinitionIndex(int capacity) { - _index = new Dictionary(capacity, ReferenceEqualityComparer.Instance); - _rows = new List(capacity); + _index = new SegmentedDictionary(capacity, ReferenceEqualityComparer.Instance); + _rows = new SegmentedList(capacity); } public bool TryGetValue(T item, out int rowId) diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index efc9fa1db1573..e3791fc94ce01 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -424,7 +424,7 @@ private bool IsMinimalDelta private object[] _pseudoSymbolTokenToReferenceMap; private UserStringHandle[] _pseudoStringTokenToTokenMap; private bool _userStringTokenOverflow; - private List _pseudoStringTokenToStringMap; + private string[] _pseudoStringTokenToStringMap; private ReferenceIndexer _referenceVisitor; protected readonly MetadataBuilder metadata; @@ -489,8 +489,8 @@ private void CreateIndices() private void CreateUserStringIndices() { - _pseudoStringTokenToStringMap = [.. module.GetStrings()]; - _pseudoStringTokenToTokenMap = new UserStringHandle[_pseudoStringTokenToStringMap.Count]; + _pseudoStringTokenToStringMap = module.CopyStrings(); + _pseudoStringTokenToTokenMap = new UserStringHandle[_pseudoStringTokenToStringMap.Length]; } private void CreateIndicesForModule() diff --git a/src/Compilers/Core/Portable/PublicAPI.Shipped.txt b/src/Compilers/Core/Portable/PublicAPI.Shipped.txt index c7de3bf377049..496cc20f14b21 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Shipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Shipped.txt @@ -2542,7 +2542,7 @@ Microsoft.CodeAnalysis.SourceProductionContext.SourceProductionContext() -> void Microsoft.CodeAnalysis.SourceReferenceResolver Microsoft.CodeAnalysis.SourceReferenceResolver.SourceReferenceResolver() -> void Microsoft.CodeAnalysis.SpecialType -Microsoft.CodeAnalysis.SpecialType.Count = 47 -> Microsoft.CodeAnalysis.SpecialType +Microsoft.CodeAnalysis.SpecialType.Count = 46 -> Microsoft.CodeAnalysis.SpecialType Microsoft.CodeAnalysis.SpecialType.None = 0 -> Microsoft.CodeAnalysis.SpecialType Microsoft.CodeAnalysis.SpecialType.System_ArgIterator = 37 -> Microsoft.CodeAnalysis.SpecialType Microsoft.CodeAnalysis.SpecialType.System_Array = 23 -> Microsoft.CodeAnalysis.SpecialType diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index f2e84ea506234..e5fe82d74f013 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -13,7 +13,6 @@ Microsoft.CodeAnalysis.Operations.ISpreadOperation Microsoft.CodeAnalysis.Operations.ISpreadOperation.ElementConversion.get -> Microsoft.CodeAnalysis.Operations.CommonConversion Microsoft.CodeAnalysis.Operations.ISpreadOperation.ElementType.get -> Microsoft.CodeAnalysis.ITypeSymbol? Microsoft.CodeAnalysis.Operations.ISpreadOperation.Operand.get -> Microsoft.CodeAnalysis.IOperation! -Microsoft.CodeAnalysis.SpecialType.System_ReadOnlySpan_T = 47 -> Microsoft.CodeAnalysis.SpecialType Microsoft.CodeAnalysis.SeparatedSyntaxList Microsoft.CodeAnalysis.SyntaxList static Microsoft.CodeAnalysis.SeparatedSyntaxList.Create(System.ReadOnlySpan nodes) -> Microsoft.CodeAnalysis.SeparatedSyntaxList diff --git a/src/Compilers/Core/Portable/SpecialMember.cs b/src/Compilers/Core/Portable/SpecialMember.cs index b75843664d326..639018f55b7c9 100644 --- a/src/Compilers/Core/Portable/SpecialMember.cs +++ b/src/Compilers/Core/Portable/SpecialMember.cs @@ -28,6 +28,7 @@ internal enum SpecialMember System_String__Length, System_String__Chars, System_String__Format, + System_String__Format_IFormatProvider, System_String__Substring, System_String__op_Implicit_ToReadOnlySpanOfChar, @@ -39,6 +40,8 @@ internal enum SpecialMember System_Delegate__Remove, System_Delegate__op_Equality, System_Delegate__op_Inequality, + System_Delegate__CreateDelegate, + System_Delegate__CreateDelegate4, System_Decimal__Zero, System_Decimal__MinusOne, @@ -167,6 +170,29 @@ internal enum SpecialMember System_ReadOnlySpan_T__ctor_Reference, + System_Collections_Generic_IReadOnlyCollection_T__Count, + System_Collections_Generic_IReadOnlyList_T__get_Item, + System_Collections_Generic_ICollection_T__Count, + System_Collections_Generic_ICollection_T__IsReadOnly, + System_Collections_Generic_ICollection_T__Add, + System_Collections_Generic_ICollection_T__Clear, + System_Collections_Generic_ICollection_T__Contains, + System_Collections_Generic_ICollection_T__CopyTo, + System_Collections_Generic_ICollection_T__Remove, + System_Collections_Generic_IList_T__get_Item, + System_Collections_Generic_IList_T__IndexOf, + System_Collections_Generic_IList_T__Insert, + System_Collections_Generic_IList_T__RemoveAt, + + System_Reflection_MethodBase__GetMethodFromHandle, + System_Reflection_MethodBase__GetMethodFromHandle2, + + System_Array__get_Length, + System_Array__Empty, + System_Array__SetValue, + + System_Type__GetTypeFromHandle, + Count } } diff --git a/src/Compilers/Core/Portable/SpecialMembers.cs b/src/Compilers/Core/Portable/SpecialMembers.cs index 776dfbb7d5b0b..887f7aa8f6960 100644 --- a/src/Compilers/Core/Portable/SpecialMembers.cs +++ b/src/Compilers/Core/Portable/SpecialMembers.cs @@ -103,10 +103,10 @@ static SpecialMembers() 0, // Arity 2, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, @@ -116,13 +116,13 @@ static SpecialMembers() 0, // Arity 3, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, @@ -132,16 +132,16 @@ static SpecialMembers() 0, // Arity 4, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, @@ -187,6 +187,16 @@ static SpecialMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, + // System_String__Format_IFormatProvider + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)SpecialType.System_String, // DeclaringTypeId + 0, // Arity + 3, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_IFormatProvider, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, + (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, + // System_String__Substring (byte)MemberFlags.Method, // Flags (byte)SpecialType.System_String, // DeclaringTypeId @@ -201,7 +211,7 @@ static SpecialMembers() (byte)SpecialType.System_String, // DeclaringTypeId 0, // Arity 1, // Method Signature - (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_ReadOnlySpan_T, + (byte)SignatureTypeCode.GenericTypeInstance, (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_ReadOnlySpan_T, 1, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Char, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, @@ -258,6 +268,27 @@ static SpecialMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Delegate, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Delegate, + // System_Delegate__CreateDelegate + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)SpecialType.System_Delegate, // DeclaringTypeId + 0, // Arity + 3, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Delegate, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Type, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Reflection_MethodInfo, + + // System_Delegate__CreateDelegate4 + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)SpecialType.System_Delegate, // DeclaringTypeId + 0, // Arity + 4, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Delegate, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Type, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Reflection_MethodInfo, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, + // System_Decimal__Zero (byte)(MemberFlags.Field | MemberFlags.Static), // Flags (byte)SpecialType.System_Decimal, // DeclaringTypeId @@ -1121,11 +1152,161 @@ static SpecialMembers() // System_ReadOnlySpan_T__ctor_Reference (byte)MemberFlags.Constructor, // Flags - (byte)SpecialType.System_ReadOnlySpan_T, // DeclaringTypeId + (byte)InternalSpecialType.System_ReadOnlySpan_T, // DeclaringTypeId 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type (byte)SignatureTypeCode.ByReference, (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Collections_Generic_IReadOnlyCollection_T__Count + (byte)(MemberFlags.Property | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_IReadOnlyCollection_T, // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type + + // System_Collections_Generic_IReadOnlyList_T__get_Item + (byte)(MemberFlags.PropertyGet | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_IReadOnlyList_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.GenericTypeParameter, 0, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Collections_Generic_ICollection_T__Count + (byte)(MemberFlags.Property | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type + + // System_Collections_Generic_ICollection_T__IsReadOnly + (byte)(MemberFlags.Property | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type + + // System_Collections_Generic_ICollection_T__Add + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Collections_Generic_ICollection_T__Clear + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + + // System_Collections_Generic_ICollection_T__Contains + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type + (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Collections_Generic_ICollection_T__CopyTo + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 2, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericTypeParameter, 0, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Collections_Generic_ICollection_T__Remove + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type + (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Collections_Generic_IList_T__get_Item + (byte)(MemberFlags.PropertyGet | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.GenericTypeParameter, 0, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Collections_Generic_IList_T__IndexOf + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type + (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Collections_Generic_IList_T__Insert + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId + 0, // Arity + 2, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + (byte)SignatureTypeCode.GenericTypeParameter, 0, + + // System_Collections_Generic_IList_T__RemoveAt + (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags + (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Reflection_MethodBase__GetMethodFromHandle + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)InternalSpecialType.System_Reflection_MethodBase, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Reflection_MethodBase, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeMethodHandle, + + // System_Reflection_MethodBase__GetMethodFromHandle2 + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)InternalSpecialType.System_Reflection_MethodBase, // DeclaringTypeId + 0, // Arity + 2, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Reflection_MethodBase, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeMethodHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeTypeHandle, + + // System_Array__get_Length + (byte)MemberFlags.PropertyGet, // Flags + (byte)SpecialType.System_Array, // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type + + // System_Array__Empty + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)SpecialType.System_Array, // DeclaringTypeId + 1, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericMethodParameter, 0, // Return Type + + // System_Array__SetValue + (byte)MemberFlags.Method, // Flags + (byte)SpecialType.System_Array, // DeclaringTypeId + 0, // Arity + 2, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, + + // System_Type__GetTypeFromHandle + (byte)(MemberFlags.Method | MemberFlags.Static), // Flags + (byte)InternalSpecialType.System_Type, // DeclaringTypeId + 0, // Arity + 1, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)InternalSpecialType.System_Type, // Return Type + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeTypeHandle, }; string[] allNames = new string[(int)SpecialMember.Count] @@ -1147,6 +1328,7 @@ static SpecialMembers() "get_Length", // System_String__Length "get_Chars", // System_String__Chars "Format", // System_String__Format + "Format", // System_String__Format_IFormatProvider "Substring", // System_String__Substring "op_Implicit", // System_String__op_Implicit_ToReadOnlySpanOfChar "IsNaN", // System_Double__IsNaN @@ -1155,6 +1337,8 @@ static SpecialMembers() "Remove", // System_Delegate__Remove "op_Equality", // System_Delegate__op_Equality "op_Inequality", // System_Delegate__op_Inequality + "CreateDelegate", // System_Delegate__CreateDelegate + "CreateDelegate", // System_Delegate__CreateDelegate4 "Zero", // System_Decimal__Zero "MinusOne", // System_Decimal__MinusOne "One", // System_Decimal__One @@ -1264,6 +1448,25 @@ static SpecialMembers() ".ctor", // System_Runtime_CompilerServices_PreserveBaseOverridesAttribute__ctor ".ctor", // System_Runtime_CompilerServices_InlineArrayAttribute__ctor ".ctor", // System_ReadOnlySpan_T__ctor_Reference + "Count", // System_Collections_Generic_IReadOnlyCollection_T__Count, + "get_Item", // System_Collections_Generic_IReadOnlyList_T__get_Item, + "Count", // System_Collections_Generic_ICollection_T__Count, + "IsReadOnly", // System_Collections_Generic_ICollection_T__IsReadOnly, + "Add", // System_Collections_Generic_ICollection_T__Add, + "Clear", // System_Collections_Generic_ICollection_T__Clear, + "Contains", // System_Collections_Generic_ICollection_T__Contains, + "CopyTo", // System_Collections_Generic_ICollection_T__CopyTo, + "Remove", // System_Collections_Generic_ICollection_T__Remove, + "get_Item", // System_Collections_Generic_IList_T__get_Item, + "IndexOf", // System_Collections_Generic_IList_T__IndexOf, + "Insert", // System_Collections_Generic_IList_T__Insert, + "RemoveAt", // System_Collections_Generic_IList_T__RemoveAt, + "GetMethodFromHandle", // System_Reflection_MethodBase__GetMethodFromHandle + "GetMethodFromHandle", // System_Reflection_MethodBase__GetMethodFromHandle2 + "get_Length", // System_Array__get_Length + "Empty", // System_Array__Empty + "SetValue", // System_Array__SetValue + "GetTypeFromHandle", // System_Type__GetTypeFromHandle }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); diff --git a/src/Compilers/Core/Portable/SpecialType.cs b/src/Compilers/Core/Portable/SpecialType.cs index aa98a07955669..94d6c4866c5d4 100644 --- a/src/Compilers/Core/Portable/SpecialType.cs +++ b/src/Compilers/Core/Portable/SpecialType.cs @@ -263,22 +263,12 @@ public enum SpecialType : sbyte /// System_Runtime_CompilerServices_InlineArrayAttribute = 46, - /// - /// Indicates that the type is from the COR library. - /// - /// - /// Check for this special type cannot be used to find the "canonical" definition of - /// since it is fully legal for it to come from sources other than the COR library, e.g. from `System.Memory` package. - /// The special type entry mostly exist so that compiler can tell this type apart when resolving other members of the COR library - /// - System_ReadOnlySpan_T = 47, - /// /// Count of special types. This is not a count of enum members. /// /// /// The underlying numeric value of this member is expected to change every time a new special type is added /// - Count = System_ReadOnlySpan_T + Count = System_Runtime_CompilerServices_InlineArrayAttribute } } diff --git a/src/Compilers/Core/Portable/SpecialTypeExtensions.cs b/src/Compilers/Core/Portable/SpecialTypeExtensions.cs index afa94c7888812..553126db2e869 100644 --- a/src/Compilers/Core/Portable/SpecialTypeExtensions.cs +++ b/src/Compilers/Core/Portable/SpecialTypeExtensions.cs @@ -87,7 +87,6 @@ public static bool IsValueType(this SpecialType specialType) case SpecialType.System_RuntimeFieldHandle: case SpecialType.System_RuntimeMethodHandle: case SpecialType.System_RuntimeTypeHandle: - case SpecialType.System_ReadOnlySpan_T: return true; default: return false; diff --git a/src/Compilers/Core/Portable/SpecialTypes.cs b/src/Compilers/Core/Portable/SpecialTypes.cs index 0d1ae1dd58ab7..d4bca2aef4b50 100644 --- a/src/Compilers/Core/Portable/SpecialTypes.cs +++ b/src/Compilers/Core/Portable/SpecialTypes.cs @@ -16,10 +16,10 @@ internal static class SpecialTypes /// that we could use ids to index into the array /// /// - private static readonly string?[] s_emittedNames = new string?[(int)SpecialType.Count + 1] + private static readonly string?[] s_emittedNames = new string?[(int)InternalSpecialType.NextAvailable] { // The following things should be in sync: - // 1) SpecialType enum + // 1) SpecialType/InternalSpecialType enum // 2) names in SpecialTypes.EmittedNames array. // 3) languageNames in SemanticFacts.cs // 4) languageNames in SemanticFacts.vb @@ -71,16 +71,20 @@ internal static class SpecialTypes "System.Runtime.CompilerServices.PreserveBaseOverridesAttribute", "System.Runtime.CompilerServices.InlineArrayAttribute", "System.ReadOnlySpan`1", + "System.IFormatProvider", + "System.Type", + "System.Reflection.MethodBase", + "System.Reflection.MethodInfo", }; - private static readonly Dictionary s_nameToTypeIdMap; + private static readonly Dictionary s_nameToTypeIdMap; private static readonly Microsoft.Cci.PrimitiveTypeCode[] s_typeIdToTypeCodeMap; private static readonly SpecialType[] s_typeCodeToTypeIdMap; static SpecialTypes() { - s_nameToTypeIdMap = new Dictionary((int)SpecialType.Count); + s_nameToTypeIdMap = new Dictionary((int)InternalSpecialType.NextAvailable - 1); int i; @@ -89,7 +93,7 @@ static SpecialTypes() string? name = s_emittedNames[i]; RoslynDebug.Assert(name is object); Debug.Assert(name.IndexOf('+') < 0); // Compilers aren't prepared to lookup for a nested special type. - s_nameToTypeIdMap.Add(name, (SpecialType)i); + s_nameToTypeIdMap.Add(name, (ExtendedSpecialType)i); } s_typeIdToTypeCodeMap = new Microsoft.Cci.PrimitiveTypeCode[(int)SpecialType.Count + 1]; @@ -144,21 +148,21 @@ static SpecialTypes() /// /// Gets the name of the special type as it would appear in metadata. /// - public static string? GetMetadataName(this SpecialType id) + public static string? GetMetadataName(this ExtendedSpecialType id) { return s_emittedNames[(int)id]; } - public static SpecialType GetTypeFromMetadataName(string metadataName) + public static ExtendedSpecialType GetTypeFromMetadataName(string metadataName) { - SpecialType id; + ExtendedSpecialType id; if (s_nameToTypeIdMap.TryGetValue(metadataName, out id)) { return id; } - return SpecialType.None; + return default; } public static SpecialType GetTypeFromMetadataName(Microsoft.Cci.PrimitiveTypeCode typeCode) diff --git a/src/Compilers/Core/Portable/Text/ChangedText.cs b/src/Compilers/Core/Portable/Text/ChangedText.cs index 0422c82896e6f..c8f7d46b8d71d 100644 --- a/src/Compilers/Core/Portable/Text/ChangedText.cs +++ b/src/Compilers/Core/Portable/Text/ChangedText.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Text; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -278,8 +279,10 @@ protected override TextLineCollection GetLinesCore() } // compute line starts given changes and line starts already computed from previous text - var lineStarts = ArrayBuilder.GetInstance(oldLineInfo.Count); - lineStarts.Add(0); + var lineStarts = new SegmentedList(capacity: oldLineInfo.Count) + { + 0 + }; // position in the original document var position = 0; @@ -299,7 +302,7 @@ protected override TextLineCollection GetLinesCore() { // remove last added line start (it was due to previous CR) // a new line start including the LF will be added next - lineStarts.RemoveLast(); + lineStarts.RemoveAt(lineStarts.Count - 1); } var lps = oldLineInfo.GetLinePositionSpan(TextSpan.FromBounds(position, change.Span.Start)); @@ -328,7 +331,7 @@ protected override TextLineCollection GetLinesCore() { // remove last added line start (it was due to previous CR) // a new line start including the LF will be added next - lineStarts.RemoveLast(); + lineStarts.RemoveAt(lineStarts.Count - 1); } // Skip first line (it is always at offset 0 and corresponds to the previous line) @@ -351,7 +354,7 @@ protected override TextLineCollection GetLinesCore() { // remove last added line start (it was due to previous CR) // a new line start including the LF will be added next - lineStarts.RemoveLast(); + lineStarts.RemoveAt(lineStarts.Count - 1); } var lps = oldLineInfo.GetLinePositionSpan(TextSpan.FromBounds(position, oldText.Length)); @@ -361,7 +364,7 @@ protected override TextLineCollection GetLinesCore() } } - return new LineInfo(this, lineStarts.ToArrayAndFree()); + return new LineInfo(this, lineStarts); } internal static class TestAccessor diff --git a/src/Compilers/Core/Portable/Text/LargeText.cs b/src/Compilers/Core/Portable/Text/LargeText.cs index b370fffd8d1ec..293623460eaef 100644 --- a/src/Compilers/Core/Portable/Text/LargeText.cs +++ b/src/Compilers/Core/Portable/Text/LargeText.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; using System.Diagnostics; +using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Text { @@ -232,12 +233,12 @@ protected override TextLineCollection GetLinesCore() return new LineInfo(this, ParseLineStarts()); } - private int[] ParseLineStarts() + private SegmentedList ParseLineStarts() { var position = 0; var index = 0; var lastCr = -1; - var arrayBuilder = ArrayBuilder.GetInstance(); + var list = new SegmentedList(); // The following loop goes through every character in the text. It is highly // performance critical, and thus inlines knowledge about common line breaks @@ -275,7 +276,7 @@ private int[] ParseLineStarts() case '\u2028': case '\u2029': line_break: - arrayBuilder.Add(position); + list.Add(position); position = index; break; } @@ -283,8 +284,8 @@ private int[] ParseLineStarts() } // Create a start for the final line. - arrayBuilder.Add(position); - return arrayBuilder.ToArrayAndFree(); + list.Add(position); + return list; } } } diff --git a/src/Compilers/Core/Portable/Text/SourceText.cs b/src/Compilers/Core/Portable/Text/SourceText.cs index 5e9ad992716a1..4f5421e1c0053 100644 --- a/src/Compilers/Core/Portable/Text/SourceText.cs +++ b/src/Compilers/Core/Portable/Text/SourceText.cs @@ -15,6 +15,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -942,28 +943,28 @@ protected virtual TextLineCollection GetLinesCore() internal sealed class LineInfo : TextLineCollection { private readonly SourceText _text; - private readonly int[] _lineStarts; + private readonly SegmentedList _lineStarts; private int _lastLineNumber; - public LineInfo(SourceText text, int[] lineStarts) + public LineInfo(SourceText text, SegmentedList lineStarts) { _text = text; _lineStarts = lineStarts; } - public override int Count => _lineStarts.Length; + public override int Count => _lineStarts.Count; public override TextLine this[int index] { get { - if (index < 0 || index >= _lineStarts.Length) + if (index < 0 || index >= _lineStarts.Count) { throw new ArgumentOutOfRangeException(nameof(index)); } int start = _lineStarts[index]; - if (index == _lineStarts.Length - 1) + if (index == _lineStarts.Count - 1) { return TextLine.FromSpan(_text, TextSpan.FromBounds(start, _text.Length)); } @@ -989,7 +990,7 @@ public override int IndexOf(int position) var lastLineNumber = _lastLineNumber; if (position >= _lineStarts[lastLineNumber]) { - var limit = Math.Min(_lineStarts.Length, lastLineNumber + 4); + var limit = Math.Min(_lineStarts.Count, lastLineNumber + 4); for (int i = lastLineNumber; i < limit; i++) { if (position < _lineStarts[i]) @@ -1040,16 +1041,18 @@ private void EnumerateChars(Action action) s_charArrayPool.Free(buffer); } - private int[] ParseLineStarts() + private SegmentedList ParseLineStarts() { // Corner case check if (0 == this.Length) { - return new[] { 0 }; + return [0]; } - var lineStarts = ArrayBuilder.GetInstance(); - lineStarts.Add(0); // there is always the first line + var lineStarts = new SegmentedList() + { + 0 // there is always the first line + }; var lastWasCR = false; @@ -1107,7 +1110,7 @@ private int[] ParseLineStarts() } }); - return lineStarts.ToArrayAndFree(); + return lineStarts; } #endregion diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 2c453ce897083..dbcb737b52e09 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -7,15 +7,9 @@ namespace Microsoft.CodeAnalysis // Members of well known types internal enum WellKnownMember { - System_Object__ToString, - System_Math__RoundDouble, System_Math__PowDoubleDouble, - System_Array__get_Length, - System_Array__Empty, - System_Array__SetValue, - System_Convert__ToBooleanDecimal, System_Convert__ToBooleanInt32, System_Convert__ToBooleanUInt32, @@ -65,8 +59,6 @@ internal enum WellKnownMember System_Reflection_MethodBase__GetMethodFromHandle, System_Reflection_MethodBase__GetMethodFromHandle2, System_Reflection_MethodInfo__CreateDelegate, - System_Delegate__CreateDelegate, - System_Delegate__CreateDelegate4, System_Reflection_FieldInfo__GetFieldFromHandle, System_Reflection_FieldInfo__GetFieldFromHandle2, @@ -431,8 +423,6 @@ internal enum WellKnownMember System_Runtime_CompilerServices_TupleElementNamesAttribute__ctorTransformNames, - System_String__Format_IFormatProvider, - Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningMultipleFiles, @@ -608,20 +598,6 @@ internal enum WellKnownMember System_Collections_IList__Remove, System_Collections_IList__RemoveAt, - System_Collections_Generic_IReadOnlyCollection_T__Count, - System_Collections_Generic_IReadOnlyList_T__get_Item, - System_Collections_Generic_ICollection_T__Count, - System_Collections_Generic_ICollection_T__IsReadOnly, - System_Collections_Generic_ICollection_T__Add, - System_Collections_Generic_ICollection_T__Clear, - System_Collections_Generic_ICollection_T__Contains, - System_Collections_Generic_ICollection_T__CopyTo, - System_Collections_Generic_ICollection_T__Remove, - System_Collections_Generic_IList_T__get_Item, - System_Collections_Generic_IList_T__IndexOf, - System_Collections_Generic_IList_T__Insert, - System_Collections_Generic_IList_T__RemoveAt, - System_Collections_Generic_List_T__ctor, System_Collections_Generic_List_T__ctorInt32, System_Collections_Generic_List_T__Add, diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index 5bd237bc8f986..351d772afc467 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.RuntimeMembers; @@ -16,13 +17,6 @@ static WellKnownMembers() { byte[] initializationBytes = new byte[] { - // System_Object__ToString - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Object, // DeclaringTypeId - 0, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, // Return Type - // System_Math__RoundDouble (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Math, // DeclaringTypeId @@ -40,29 +34,6 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Double, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Double, - // System_Array__get_Length - (byte)MemberFlags.PropertyGet, // Flags - (byte)WellKnownType.System_Array, // DeclaringTypeId - 0, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type - - // System_Array__Empty - (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)WellKnownType.System_Array, // DeclaringTypeId - 1, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericMethodParameter, 0, // Return Type - - // System_Array__SetValue - (byte)MemberFlags.Method, // Flags - (byte)WellKnownType.System_Array, // DeclaringTypeId - 0, // Arity - 2, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - // System_Convert__ToBooleanDecimal (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Convert, // DeclaringTypeId @@ -364,7 +335,7 @@ static WellKnownMembers() 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Type, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeTypeHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeTypeHandle, // System_Type__Missing (byte)(MemberFlags.Field | MemberFlags.Static), // Flags @@ -403,7 +374,7 @@ static WellKnownMembers() 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_MethodBase, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeMethodHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeMethodHandle, // System_Reflection_MethodBase__GetMethodFromHandle2 (byte)(MemberFlags.Method | MemberFlags.Static), // Flags @@ -411,8 +382,8 @@ static WellKnownMembers() 0, // Arity 2, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_MethodBase, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeMethodHandle, - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeTypeHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeMethodHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeTypeHandle, // System_Reflection_MethodInfo__CreateDelegate (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags @@ -423,34 +394,13 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Type, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, - // System_Delegate__CreateDelegate - (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)SpecialType.System_Delegate, // DeclaringTypeId - 0, // Arity - 3, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Delegate, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Type, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_MethodInfo, - - // System_Delegate__CreateDelegate4 - (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)SpecialType.System_Delegate, // DeclaringTypeId - 0, // Arity - 4, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Delegate, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Type, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_MethodInfo, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, - // System_Reflection_FieldInfo__GetFieldFromHandle (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.System_Reflection_FieldInfo, // DeclaringTypeId 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_FieldInfo, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeFieldHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeFieldHandle, // System_Reflection_FieldInfo__GetFieldFromHandle2 (byte)(MemberFlags.Method | MemberFlags.Static), // Flags @@ -458,8 +408,8 @@ static WellKnownMembers() 0, // Arity 2, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_Reflection_FieldInfo, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeFieldHandle, - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeTypeHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeFieldHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeTypeHandle, // System_Reflection_Missing__Value (byte)(MemberFlags.Field | MemberFlags.Static), // Flags @@ -1041,7 +991,7 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), 1, (byte)SignatureTypeCode.GenericMethodParameter, 0, - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeFieldHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeFieldHandle, // System_Runtime_CompilerServices_RuntimeHelpers__GetObjectValueObject (byte)(MemberFlags.Method | MemberFlags.Static), // Flags @@ -1058,7 +1008,7 @@ static WellKnownMembers() 2, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Array, - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_RuntimeFieldHandle, + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_RuntimeFieldHandle, // System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData (byte)(MemberFlags.PropertyGet | MemberFlags.Static), // Flags @@ -2663,7 +2613,7 @@ static WellKnownMembers() // System_ValueTuple_T1__Item1 (byte)MemberFlags.Field, // Flags - (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ValueTuple_T1 - WellKnownType.ExtSentinel), // DeclaringTypeId + (byte)WellKnownType.System_ValueTuple_T1, // DeclaringTypeId 0, // Arity (byte)SignatureTypeCode.GenericTypeParameter, 0, // Field Signature @@ -2879,7 +2829,7 @@ static WellKnownMembers() // System_ValueTuple_T1__ctor (byte)MemberFlags.Constructor, // Flags - (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ValueTuple_T1 - WellKnownType.ExtSentinel), // DeclaringTypeId + (byte)WellKnownType.System_ValueTuple_T1, // DeclaringTypeId 0, // Arity 1, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type @@ -2978,16 +2928,6 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, - // System_String__Format_IFormatProvider - (byte)(MemberFlags.Method | MemberFlags.Static), // Flags - (byte)SpecialType.System_String, // DeclaringTypeId - 0, // Arity - 3, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)WellKnownType.System_IFormatProvider, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, - (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Object, - // Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile (byte)(MemberFlags.Method | MemberFlags.Static), // Flags (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation - WellKnownType.ExtSentinel), // DeclaringTypeId @@ -4201,108 +4141,6 @@ static WellKnownMembers() (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - // System_Collections_Generic_IReadOnlyCollection_T__Count - (byte)(MemberFlags.Property | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_IReadOnlyCollection_T, // DeclaringTypeId - 0, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type - - // System_Collections_Generic_IReadOnlyList_T__get_Item - (byte)(MemberFlags.PropertyGet | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_IReadOnlyList_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.GenericTypeParameter, 0, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - - // System_Collections_Generic_ICollection_T__Count - (byte)(MemberFlags.Property | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type - - // System_Collections_Generic_ICollection_T__IsReadOnly - (byte)(MemberFlags.Property | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type - - // System_Collections_Generic_ICollection_T__Add - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - (byte)SignatureTypeCode.GenericTypeParameter, 0, - - // System_Collections_Generic_ICollection_T__Clear - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 0, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - - // System_Collections_Generic_ICollection_T__Contains - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type - (byte)SignatureTypeCode.GenericTypeParameter, 0, - - // System_Collections_Generic_ICollection_T__CopyTo - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 2, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - (byte)SignatureTypeCode.SZArray, (byte)SignatureTypeCode.GenericTypeParameter, 0, - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - - // System_Collections_Generic_ICollection_T__Remove - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_ICollection_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Boolean, // Return Type - (byte)SignatureTypeCode.GenericTypeParameter, 0, - - // System_Collections_Generic_IList_T__get_Item - (byte)(MemberFlags.PropertyGet | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.GenericTypeParameter, 0, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - - // System_Collections_Generic_IList_T__IndexOf - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, // Return Type - (byte)SignatureTypeCode.GenericTypeParameter, 0, - - // System_Collections_Generic_IList_T__Insert - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId - 0, // Arity - 2, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - (byte)SignatureTypeCode.GenericTypeParameter, 0, - - // System_Collections_Generic_IList_T__RemoveAt - (byte)(MemberFlags.Method | MemberFlags.Virtual), // Flags - (byte)SpecialType.System_Collections_Generic_IList_T, // DeclaringTypeId - 0, // Arity - 1, // Method Signature - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type - (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32, - // System_Collections_Generic_List_T__ctor (byte)MemberFlags.Constructor, // Flags (byte)WellKnownType.System_Collections_Generic_List_T, // DeclaringTypeId @@ -4477,12 +4315,8 @@ static WellKnownMembers() string[] allNames = new string[(int)WellKnownMember.Count] { - "ToString", // System_Object__ToString "Round", // System_Math__RoundDouble "Pow", // System_Math__PowDoubleDouble - "get_Length", // System_Array__get_Length - "Empty", // System_Array__Empty - "SetValue", // System_Array__SetValue "ToBoolean", // System_Convert__ToBooleanDecimal "ToBoolean", // System_Convert__ToBooleanInt32 "ToBoolean", // System_Convert__ToBooleanUInt32 @@ -4528,8 +4362,6 @@ static WellKnownMembers() "GetMethodFromHandle", // System_Reflection_MethodBase__GetMethodFromHandle "GetMethodFromHandle", // System_Reflection_MethodBase__GetMethodFromHandle2 "CreateDelegate", // System_Reflection_MethodInfo__CreateDelegate - "CreateDelegate", // System_Delegate__CreateDelegate - "CreateDelegate", // System_Delegate__CreateDelegate4 "GetFieldFromHandle", // System_Reflection_FieldInfo__GetFieldFromHandle "GetFieldFromHandle", // System_Reflection_FieldInfo__GetFieldFromHandle2 "Value", // System_Reflection_Missing__Value @@ -4842,8 +4674,6 @@ static WellKnownMembers() ".ctor", // System_Runtime_CompilerServices_TupleElementNamesAttribute__ctorTransformNames - "Format", // System_String__Format_IFormatProvider - "CreatePayload", // Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile "CreatePayload", // Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningMultipleFiles @@ -4994,19 +4824,6 @@ static WellKnownMembers() "Insert", // System_Collections_IList__Insert, "Remove", // System_Collections_IList__Remove, "RemoveAt", // System_Collections_IList__RemoveAt, - "Count", // System_Collections_Generic_IReadOnlyCollection_T__Count, - "get_Item", // System_Collections_Generic_IReadOnlyList_T__get_Item, - "Count", // System_Collections_Generic_ICollection_T__Count, - "IsReadOnly", // System_Collections_Generic_ICollection_T__IsReadOnly, - "Add", // System_Collections_Generic_ICollection_T__Add, - "Clear", // System_Collections_Generic_ICollection_T__Clear, - "Contains", // System_Collections_Generic_ICollection_T__Contains, - "CopyTo", // System_Collections_Generic_ICollection_T__CopyTo, - "Remove", // System_Collections_Generic_ICollection_T__Remove, - "get_Item", // System_Collections_Generic_IList_T__get_Item, - "IndexOf", // System_Collections_Generic_IList_T__IndexOf, - "Insert", // System_Collections_Generic_IList_T__Insert, - "RemoveAt", // System_Collections_Generic_IList_T__RemoveAt, ".ctor", // System_Collections_Generic_List_T__ctor, ".ctor", // System_Collections_Generic_List_T__ctorInt32, "Add", // System_Collections_Generic_List_T__Add @@ -5029,6 +4846,13 @@ static WellKnownMembers() }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); + +#if DEBUG + foreach (var descriptor in s_descriptors) + { + Debug.Assert(!descriptor.IsSpecialTypeMember); // Members of types from core library should be in the SpecialMember set instead. + } +#endif } public static MemberDescriptor GetDescriptor(WellKnownMember member) diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 2b93fedb5b26a..c9ecb5758d173 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -17,11 +17,10 @@ internal enum WellKnownType // Value 0 represents an unknown type Unknown = SpecialType.None, - First = SpecialType.Count + 1, + First = InternalSpecialType.NextAvailable, // The following type ids should be in sync with names in WellKnownTypes.metadataNames array. System_Math = First, - System_Array, System_Attribute, System_CLSCompliantAttribute, System_Convert, @@ -30,9 +29,6 @@ internal enum WellKnownType System_FormattableString, System_Guid, System_IFormattable, - System_RuntimeTypeHandle, - System_RuntimeFieldHandle, - System_RuntimeMethodHandle, System_MarshalByRefObject, System_Type, System_Reflection_AssemblyKeyFileAttribute, @@ -246,15 +242,15 @@ internal enum WellKnownType System_Environment, System_Runtime_GCLatencyMode, - System_IFormatProvider, - CSharp7Sentinel = System_IFormatProvider, // all types that were known before CSharp7 should remain above this sentinel + CSharp7Sentinel = System_Runtime_GCLatencyMode, // all types that were known before CSharp7 should remain above this sentinel System_ValueTuple, + System_ValueTuple_T1, + ExtSentinel, // Not a real type, just a marker for types above 255 and strictly below 512 - System_ValueTuple_T1, System_ValueTuple_T2, System_ValueTuple_T3, System_ValueTuple_T4, @@ -357,10 +353,9 @@ internal static class WellKnownTypes /// that we could use ids to index into the array /// /// - private static readonly string[] s_metadataNames = new string[] + private static readonly string[] s_metadataNames = new string[Count] { "System.Math", - "System.Array", "System.Attribute", "System.CLSCompliantAttribute", "System.Convert", @@ -369,9 +364,6 @@ internal static class WellKnownTypes "System.FormattableString", "System.Guid", "System.IFormattable", - "System.RuntimeTypeHandle", - "System.RuntimeFieldHandle", - "System.RuntimeMethodHandle", "System.MarshalByRefObject", "System.Type", "System.Reflection.AssemblyKeyFileAttribute", @@ -581,13 +573,11 @@ internal static class WellKnownTypes "System.Runtime.GCLatencyMode", - "System.IFormatProvider", - "System.ValueTuple", + "System.ValueTuple`1", - "", // extension marker + "", // WellKnownType.ExtSentinel extension marker - "System.ValueTuple`1", "System.ValueTuple`2", "System.ValueTuple`3", "System.ValueTuple`4", @@ -702,7 +692,7 @@ private static void AssertEnumAndTableInSync() typeIdName = "Microsoft.VisualBasic.CompilerServices.ObjectFlowControl+ForLoopControl"; break; case WellKnownType.CSharp7Sentinel: - typeIdName = "System.IFormatProvider"; + typeIdName = "System.Runtime.GCLatencyMode"; break; case WellKnownType.ExtSentinel: typeIdName = ""; @@ -723,8 +713,17 @@ private static void AssertEnumAndTableInSync() Debug.Assert(name == typeIdName, $"Enum name ({typeIdName}) and type name ({name}) must match at {i}"); } - Debug.Assert((int)WellKnownType.ExtSentinel == 255); - Debug.Assert((int)WellKnownType.NextAvailable <= 512, "Time for a new sentinel"); +#if DEBUG + // Some compile time asserts + { + // The WellKnownType.ExtSentinel value must be 255 + _ = new int[(int)WellKnownType.ExtSentinel - 255]; + _ = new int[255 - (int)WellKnownType.ExtSentinel]; + + // Once the last real id minus WellKnownType.ExtSentinel cannot fit into a byte, it is time to add a new sentinel. + _ = new int[255 - ((int)WellKnownType.NextAvailable - 1 - (int)WellKnownType.ExtSentinel)]; + } +#endif } public static bool IsWellKnownType(this WellKnownType typeId) diff --git a/src/Compilers/Test/Core/Diagnostics/DiagnosticDescription.cs b/src/Compilers/Test/Core/Diagnostics/DiagnosticDescription.cs index 01817d73a198a..89d683b133610 100644 --- a/src/Compilers/Test/Core/Diagnostics/DiagnosticDescription.cs +++ b/src/Compilers/Test/Core/Diagnostics/DiagnosticDescription.cs @@ -6,17 +6,18 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; -using Roslyn.Test.Utilities; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.CSharp; -using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Test.Utilities { @@ -518,7 +519,7 @@ public static string GetAssertText(DiagnosticDescription[] expected, IEnumerable for (i = 0; e.MoveNext(); i++) { Diagnostic d = e.Current; - string message = d.ToString(); + string message = d.ToString(CultureInfo.InvariantCulture); if (Regex.Match(message, @"{\d+}").Success) { Assert.True(false, "Diagnostic messages should never contain unsubstituted placeholders.\n " + message); @@ -533,7 +534,7 @@ public static string GetAssertText(DiagnosticDescription[] expected, IEnumerable { Indent(assertText, indentDepth); assertText.Append("// "); - assertText.AppendLine(d.ToString()); + assertText.AppendLine(message); var l = d.Location; if (l.IsInSource) { diff --git a/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj b/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj index bdfcfbef906ef..76c9aeab86bb0 100644 --- a/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj +++ b/src/Compilers/Test/Core/Microsoft.CodeAnalysis.Test.Utilities.csproj @@ -70,6 +70,7 @@ + diff --git a/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb b/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb index 0e0ad8b47a49d..ef58a60eb2802 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb @@ -800,7 +800,7 @@ Friend Class MockAssemblySymbol End Get End Property - Friend Overrides Function GetDeclaredSpecialType(type As SpecialType) As NamedTypeSymbol + Friend Overrides Function GetDeclaredSpecialType(type As ExtendedSpecialType) As NamedTypeSymbol Throw New NotImplementedException() End Function diff --git a/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.BinderFactoryVisitor.vb b/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.BinderFactoryVisitor.vb index d7e1b1d30bc34..6539ae3ebd236 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.BinderFactoryVisitor.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.BinderFactoryVisitor.vb @@ -2,27 +2,26 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend Class BinderFactory - Private NotInheritable Class BinderFactoryVisitor + Friend NotInheritable Class BinderFactoryVisitor Inherits VisualBasicSyntaxVisitor(Of Binder) Private _position As Integer - Private ReadOnly _factory As BinderFactory + Private _factory As BinderFactory - Public Sub New(factory As BinderFactory) + Public Sub Initialize(factory As BinderFactory, position As Integer) Me._factory = factory + Me._position = position End Sub - Friend WriteOnly Property Position As Integer - Set(value As Integer) - Me._position = value - End Set - End Property + Public Sub Clear() + _factory = Nothing + _position = 0 + End Sub Public Overrides Function VisitXmlCrefAttribute(node As XmlCrefAttributeSyntax) As Binder Dim trivia As StructuredTriviaSyntax = node.EnclosingStructuredTrivia diff --git a/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.vb b/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.vb index 7992de3ed8202..6cf92a9196215 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/BinderFactory.vb @@ -3,11 +3,8 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Concurrent -Imports System.Collections.Generic Imports System.Collections.Immutable -Imports System.Threading Imports Microsoft.CodeAnalysis.PooledObjects -Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -30,6 +27,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ' a NodeUsage to sub-distinguish binders associated with nodes. Each kind of syntax node must have its ' associated usage value(s), because the usage is used when creating the binder (if not found in the cache). Private ReadOnly _cache As ConcurrentDictionary(Of ValueTuple(Of VisualBasicSyntaxNode, Byte), Binder) + + Private Shared ReadOnly s_binderFactoryVisitorPool As ObjectPool(Of BinderFactoryVisitor) = New ObjectPool(Of BinderFactoryVisitor)(Function() New BinderFactoryVisitor()) Private ReadOnly _binderFactoryVisitorPool As ObjectPool(Of BinderFactoryVisitor) Private ReadOnly Property InScript As Boolean @@ -38,28 +37,38 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Public Sub New(sourceModule As SourceModuleSymbol, tree As SyntaxTree) + Public Sub New(sourceModule As SourceModuleSymbol, tree As SyntaxTree, Optional binderFactoryVisitorPoolOpt As ObjectPool(Of BinderFactoryVisitor) = Nothing) Me._sourceModule = sourceModule Me._tree = tree Me._cache = New ConcurrentDictionary(Of ValueTuple(Of VisualBasicSyntaxNode, Byte), Binder) - - Me._binderFactoryVisitorPool = New ObjectPool(Of BinderFactoryVisitor)(Function() New BinderFactoryVisitor(Me)) + Me._binderFactoryVisitorPool = If(binderFactoryVisitorPoolOpt, s_binderFactoryVisitorPool) End Sub Private Function MakeBinder(node As SyntaxNode, position As Integer) As Binder If SyntaxFacts.InSpanOrEffectiveTrailingOfNode(node, position) OrElse node.Kind = SyntaxKind.CompilationUnit Then - Dim visitor = _binderFactoryVisitorPool.Allocate() - visitor.Position = position + Dim visitor = GetBinderFactoryVisitor(position) Dim result = visitor.Visit(node) - _binderFactoryVisitorPool.Free(visitor) + ClearBinderFactoryVisitor(visitor) Return result End If Return Nothing End Function + Private Function GetBinderFactoryVisitor(position As Integer) As BinderFactoryVisitor + Dim visitor = _binderFactoryVisitorPool.Allocate() + visitor.Initialize(Me, position) + + Return visitor + End Function + + Private Sub ClearBinderFactoryVisitor(visitor As BinderFactoryVisitor) + visitor.Clear() + _binderFactoryVisitorPool.Free(visitor) + End Sub + ' Get binder for interior of a namespace block Public Function GetNamespaceBinder(node As NamespaceBlockSyntax) As Binder Return GetBinderForNodeAndUsage(node, NodeUsage.NamespaceBlockInterior, node.Parent, node.SpanStart) diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb index f4f2df71c9200..3d93cd5b469d9 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Expressions.vb @@ -694,7 +694,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ReportDiagnostic(diagnostics, node.Type, ErrorFactory.ErrorInfo(ERRID.ERR_VoidArrayDisallowed)) End If - Return New BoundGetType(node, typeExpression, GetWellKnownType(WellKnownType.System_Type, node, diagnostics)) + Return New BoundGetType(node, typeExpression, getTypeFromHandle:=Nothing, GetWellKnownType(WellKnownType.System_Type, node, diagnostics)) End Function Private Function BindNameOfExpression(node As NameOfExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/BoundCall.vb b/src/Compilers/VisualBasic/Portable/BoundTree/BoundCall.vb index abae4217e3566..1db8f6060bd12 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/BoundCall.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/BoundCall.vb @@ -113,7 +113,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If isLateBound Then Debug.Assert(type.IsObjectType) ElseIf Not isOperator Then - Debug.Assert(type.IsSameTypeIgnoringAll(signatureType)) + Debug.Assert(type.IsErrorType() OrElse type.IsSameTypeIgnoringAll(signatureType)) ElseIf Not isLifted.HasValue Then If type.IsSameTypeIgnoringAll(signatureType) Then isLifted = False diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml b/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml index 27e579c00e454..20716accb919f 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/VisualBasic/Portable/BoundTree/BoundNodes.xml @@ -182,6 +182,8 @@ types. --> + + @@ -196,6 +198,7 @@ + - + + + - - - - + + + + + + + + + + + + + + + + + - - + - + - - - - - + @@ -214,4 +311,14 @@ VirtualizationMode="{Binding RelativeSource={RelativeSource TemplatedParent},Path=(VirtualizingStackPanel.VirtualizationMode)}" /> + + + diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml.cs index 81482f51b862c..d34e9dcb33a8d 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml.cs @@ -102,6 +102,22 @@ public override void OnApplyTemplate() _dropDownPopup = (Popup)GetTemplateChild(DropDownPopup)!; } + private void GetSuggestionsButtonClick(object sender, RoutedEventArgs e) + { + if (_smartRenameViewModel.IsUsingResultPanel) + { + _smartRenameViewModel.IsSuggestionsPanelCollapsed = !_smartRenameViewModel.IsSuggestionsPanelCollapsed; + if (_smartRenameViewModel.IsSuggestionsPanelExpanded) + { + _smartRenameViewModel.GetSuggestionsCommand.Execute(null); + } + } + else + { + _smartRenameViewModel.GetSuggestionsCommand.Execute(null); + } + } + private void ComboBox_Unloaded(object sender, RoutedEventArgs e) { _smartRenameViewModel.SuggestedNames.CollectionChanged -= SuggestedNames_CollectionChanged; @@ -120,6 +136,10 @@ private void ComboBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEvent private void ComboBox_PreviewKeyUp(object sender, KeyEventArgs e) { + if (!_smartRenameViewModel.IsUsingDropdown) + { + return; + } if ((e.Key is Key.Up or Key.Down) && Items.Count > 0) { Assumes.NotNull(_dropDownPopup); @@ -144,6 +164,10 @@ private void ItemsPresenter_PreviewMouseUp(object sender, MouseButtonEventArgs e private void InnerTextBox_GotFocus(object sender, RoutedEventArgs e) { + if (!_smartRenameViewModel.IsUsingDropdown) + { + return; + } if (Items.Count > 0) { Assumes.NotNull(_dropDownPopup); @@ -160,7 +184,9 @@ private void InnerTextBox_LostFocus(object sender, RoutedEventArgs e) private void InnerTextBox_PreviewKeyDown(object sender, KeyEventArgs e) { Assumes.NotNull(_dropDownPopup); - if ((e.Key is Key.Escape or Key.Space or Key.Enter) && _dropDownPopup.IsOpen) + if ((e.Key is Key.Escape or Key.Space or Key.Enter) + && (_dropDownPopup.IsOpen // Handle these keystrokes when dropdown is present + || _smartRenameViewModel.IsUsingResultPanel && this.TextSelectionLength < this.Text.Length)) // Or when panel is present and text is not yet selected { _dropDownPopup.IsOpen = false; SelectAllText(); diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel.cs index 4f688e9f2ee4a..146fd082e8d13 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel.cs @@ -5,24 +5,28 @@ using System; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; +using Microsoft.CodeAnalysis.Editor.InlineRename; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.EditorFeatures.Lightup; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.PlatformUI; namespace Microsoft.CodeAnalysis.InlineRename.UI.SmartRename; -internal sealed class SmartRenameViewModel : INotifyPropertyChanged, IDisposable +internal sealed partial class SmartRenameViewModel : INotifyPropertyChanged, IDisposable { #pragma warning disable CS0618 // Editor team use Obsolete attribute to mark potential changing API private readonly ISmartRenameSessionWrapper _smartRenameSession; #pragma warning restore CS0618 + private readonly IGlobalOptionService _globalOptionService; private readonly IThreadingContext _threadingContext; private readonly IAsynchronousOperationListenerProvider _listenerProvider; private readonly CancellationTokenSource _cancellationTokenSource = new(); @@ -44,6 +48,8 @@ internal sealed class SmartRenameViewModel : INotifyPropertyChanged, IDisposable public string StatusMessage => _smartRenameSession.StatusMessage; public bool StatusMessageVisibility => _smartRenameSession.StatusMessageVisibility; + public bool IsUsingResultPanel { get; set; } + public bool IsUsingDropdown { get; set; } private string? _selectedSuggestedName; @@ -64,11 +70,41 @@ public string? SelectedSuggestedName } } - public static string GetSuggestionsTooltip => EditorFeaturesWpfResources.Get_AI_suggestions; + public bool IsSuggestionsPanelCollapsed + { + get => IsUsingDropdown || _globalOptionService.GetOption(InlineRenameUIOptionsStorage.CollapseSuggestionsPanel); + set + { + if (value != IsSuggestionsPanelCollapsed) + { + _globalOptionService.SetGlobalOption(InlineRenameUIOptionsStorage.CollapseSuggestionsPanel, value); + NotifyPropertyChanged(nameof(IsSuggestionsPanelCollapsed)); + NotifyPropertyChanged(nameof(IsSuggestionsPanelExpanded)); + } + } + } + + public bool IsSuggestionsPanelExpanded + { + get => IsUsingResultPanel && !IsSuggestionsPanelCollapsed; + } + + public string GetSuggestionsTooltip + => IsUsingDropdown + ? EditorFeaturesWpfResources.Get_AI_suggestions + : EditorFeaturesWpfResources.Toggle_AI_suggestions; + + public string SubmitTextOverride + => IsUsingDropdown + ? EditorFeaturesWpfResources.Enter_to_rename_shift_enter_to_preview_ctrl_space_for_ai_suggestion + : EditorFeaturesWpfResources.Enter_to_rename_shift_enter_to_preview; + + public static string GeneratingSuggestionsLabel => EditorFeaturesWpfResources.Generating_suggestions; public ICommand GetSuggestionsCommand { get; } public SmartRenameViewModel( + IGlobalOptionService globalOptionService, IThreadingContext threadingContext, IAsynchronousOperationListenerProvider listenerProvider, #pragma warning disable CS0618 // Editor team use Obsolete attribute to mark potential changing API @@ -76,6 +112,7 @@ public SmartRenameViewModel( #pragma warning restore CS0618, RenameFlyoutViewModel baseViewModel) { + _globalOptionService = globalOptionService; _threadingContext = threadingContext; _listenerProvider = listenerProvider; _smartRenameSession = smartRenameSession; @@ -85,15 +122,34 @@ public SmartRenameViewModel( this.BaseViewModel.IdentifierText = baseViewModel.IdentifierText; GetSuggestionsCommand = new DelegateCommand(OnGetSuggestionsCommandExecute, null, threadingContext.JoinableTaskFactory); + + var getSuggestionsAutomatically = _globalOptionService.GetOption(InlineRenameUIOptionsStorage.GetSuggestionsAutomatically); + IsUsingResultPanel = getSuggestionsAutomatically; + IsUsingDropdown = !IsUsingResultPanel; + SetupTelemetry(); + if (IsUsingResultPanel && IsSuggestionsPanelExpanded) + { + OnGetSuggestionsCommandExecute(); + } } private void OnGetSuggestionsCommandExecute() { _threadingContext.ThrowIfNotOnUIThread(); + if (IsUsingResultPanel && SuggestedNames.Count > 0) + { + // Don't get suggestions again in the automatic scenario + return; + } if (_getSuggestionsTask.Status is TaskStatus.RanToCompletion or TaskStatus.Faulted or TaskStatus.Canceled) { var listener = _listenerProvider.GetListener(FeatureAttribute.SmartRename); var listenerToken = listener.BeginAsyncOperation(nameof(_smartRenameSession.GetSuggestionsAsync)); + if (IsUsingDropdown && _suggestionsDropdownTelemetry is not null) + { + _suggestionsDropdownTelemetry.DropdownButtonClickTimes += 1; + } + _getSuggestionsTask = _smartRenameSession.GetSuggestionsAsync(_cancellationTokenSource.Token).CompletesAsyncOperation(listenerToken); } } @@ -107,8 +163,14 @@ private void SessionPropertyChanged(object sender, PropertyChangedEventArgs e) var textInputBackup = BaseViewModel.IdentifierText; SuggestedNames.Clear(); + var count = 0; foreach (var name in _smartRenameSession.SuggestedNames) { + if (++count > 3 && IsUsingResultPanel) + { + // Set limit of 3 results when using the result panel + break; + } SuggestedNames.Add(name); } @@ -144,12 +206,14 @@ public void Cancel() _cancellationTokenSource.Cancel(); // It's needed by editor-side telemetry. _smartRenameSession.OnCancel(); + PostTelemetry(isCommit: false); } public void Commit(string finalIdentifierName) { // It's needed by editor-side telemetry. _smartRenameSession.OnSuccess(finalIdentifierName); + PostTelemetry(isCommit: true); } public void Dispose() @@ -158,4 +222,7 @@ public void Dispose() _smartRenameSession.Dispose(); _cancellationTokenSource.Dispose(); } + + private void NotifyPropertyChanged([CallerMemberName] string? name = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel_Telemetry.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel_Telemetry.cs new file mode 100644 index 0000000000000..107ca90395dd3 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameViewModel_Telemetry.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Editor.InlineRename; +using Microsoft.CodeAnalysis.EditorFeatures.Lightup; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Telemetry; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.InlineRename.UI.SmartRename +{ + internal partial class SmartRenameViewModel + { + private SuggestionsPanelTelemetry? _suggestionsPanelTelemetry; + private SuggestionsDropdownTelemetry? _suggestionsDropdownTelemetry; + + private sealed class SuggestionsPanelTelemetry + { + public bool CollapseSuggestionsPanelWhenRenameStarts { get; set; } + } + + private sealed class SuggestionsDropdownTelemetry + { + public int DropdownButtonClickTimes { get; set; } + } + + private void SetupTelemetry() + { + var getSuggestionsAutomatically = _globalOptionService.GetOption(InlineRenameUIOptionsStorage.GetSuggestionsAutomatically); + if (getSuggestionsAutomatically) + { + _suggestionsPanelTelemetry = new SuggestionsPanelTelemetry + { + CollapseSuggestionsPanelWhenRenameStarts = _globalOptionService.GetOption(InlineRenameUIOptionsStorage.CollapseSuggestionsPanel) + }; + } + else + { + _suggestionsDropdownTelemetry = new SuggestionsDropdownTelemetry(); + } + } + + private void PostTelemetry(bool isCommit) + { + if (_suggestionsPanelTelemetry is not null) + { + RoslynDebug.Assert(_suggestionsDropdownTelemetry is null); + TelemetryLogging.Log(FunctionId.Copilot_Rename, KeyValueLogMessage.Create(m => + { + m[nameof(isCommit)] = isCommit; + m["UseSuggestionsPanel"] = true; + m[nameof(SuggestionsPanelTelemetry.CollapseSuggestionsPanelWhenRenameStarts)] = _suggestionsPanelTelemetry.CollapseSuggestionsPanelWhenRenameStarts; + m["CollapseSuggestionsPanelWhenRenameEnds"] = _globalOptionService.GetOption(InlineRenameUIOptionsStorage.CollapseSuggestionsPanel); + m["smartRenameSessionInProgress"] = _smartRenameSession.IsInProgress; + })); + } + else + { + RoslynDebug.Assert(_suggestionsDropdownTelemetry is not null); + TelemetryLogging.Log(FunctionId.Copilot_Rename, KeyValueLogMessage.Create(m => + { + m[nameof(isCommit)] = isCommit; + m["UseDropDown"] = true; + m[nameof(SuggestionsDropdownTelemetry.DropdownButtonClickTimes)] = _suggestionsDropdownTelemetry.DropdownButtonClickTimes; + m["smartRenameSessionInProgress"] = _smartRenameSession.IsInProgress; + })); + } + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf index 09984a6f812ca..a32bff0548d13 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf @@ -32,6 +32,11 @@ Výběr se spouští v okně Interactive. + + Generating suggestions... + Generování návrhů… + + Get AI-powered rename suggestions (Ctrl+Space) Získejte návrhy přejmenování využívající umělou inteligenci (Ctrl+Mezerník) @@ -67,6 +72,11 @@ Vlastnost CurrentWindow se dá přiřadit jenom jednou. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf index de7e4622e7435..dea61db3524ae 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf @@ -32,6 +32,11 @@ Die Auswahl wird im Interactive-Fenster ausgeführt. + + Generating suggestions... + Vorschläge werden generiert... + + Get AI-powered rename suggestions (Ctrl+Space) KI-gestützte Umbenennungsvorschläge abrufen (STRG+LEERTASTE) @@ -67,6 +72,11 @@ Die Eigenschaft "CurrentWindow" kann nur ein Mal zugewiesen werden. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf index 116149effe1f1..bcf089b6b8c2a 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf @@ -32,6 +32,11 @@ Ejecutando selección en ventana interactiva. + + Generating suggestions... + Generando sugerencias... + + Get AI-powered rename suggestions (Ctrl+Space) Obtener sugerencias de cambio de nombre con tecnología de inteligencia artificial (Ctrl+Espacio) @@ -67,6 +72,11 @@ La propiedad CurrentWindow solo se puede asignar una vez. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf index 4319062bca8d4..e678307ffa423 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf @@ -32,6 +32,11 @@ Exécution de la sélection dans la fenêtre interactive. + + Generating suggestions... + Génération en cours de suggestions... + + Get AI-powered rename suggestions (Ctrl+Space) Obtenir des suggestions de renommage basées sur l’intelligence artificielle (Ctrl+Espace) @@ -67,6 +72,11 @@ La propriété CurrentWindow ne peut être assignée qu'une seule fois. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf index 3f5deaf2491d7..1a7ded6c1e19a 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf @@ -32,6 +32,11 @@ Esecuzione della selezione nella finestra interattiva. + + Generating suggestions... + Generazione di suggerimenti in corso + + Get AI-powered rename suggestions (Ctrl+Space) Ottieni suggerimenti di ridenominazione basati sull'intelligenza artificiale (CTRL+BARRA SPAZIATRICE) @@ -67,6 +72,11 @@ La proprietà CurrentWindow può essere assegnata una sola volta. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf index fcfbc0b834c31..873425e9392c9 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf @@ -32,6 +32,11 @@ インタラクティブ ウィンドウで選択を実行します。 + + Generating suggestions... + 候補を生成しています... + + Get AI-powered rename suggestions (Ctrl+Space) AI を利用した名前変更の提示を取得する (Ctrl + Space) @@ -67,6 +72,11 @@ CurrentWindow プロパティは 1 回のみ割り当てることができます。 + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf index 5065613dadc48..e91e28175e39f 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf @@ -32,6 +32,11 @@ 선택 영역을 대화형 창에서 실행하는 중입니다. + + Generating suggestions... + 제안 사항을 생성하는 중... + + Get AI-powered rename suggestions (Ctrl+Space) AI 기반 이름 바꾸기 제안 보기(Ctrl+스페이스바) @@ -67,6 +72,11 @@ CurrentWindow 속성은 한 번만 할당될 수 있습니다. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf index 23c17d9693380..afdce182da2cb 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf @@ -32,6 +32,11 @@ Wykonywanie zaznaczenia w oknie Interactive. + + Generating suggestions... + Generowanie sugestii... + + Get AI-powered rename suggestions (Ctrl+Space) Uzyskaj funkcję Zmień nazwy sugestii obsługiwane przez sztuczną inteligencję (Ctrl+Spacja) @@ -67,6 +72,11 @@ Właściwość CurrentWindow można przypisać tylko raz. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf index f634d8dae75ed..bbb19cb2a3453 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf @@ -32,6 +32,11 @@ Executando seleção na Janela Interativa. + + Generating suggestions... + Gerando sugestões... + + Get AI-powered rename suggestions (Ctrl+Space) Obter sugestões de renomeação com tecnologia de IA (Ctrl+Espaço) @@ -67,6 +72,11 @@ A propriedade CurrentWindow deve ser atribuído apenas uma vez. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf index e2a1e6d137a96..50eb6c2ef8c5f 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf @@ -32,6 +32,11 @@ Выполнение выделенного фрагмента в Интерактивном окне. + + Generating suggestions... + Создаются предложения... + + Get AI-powered rename suggestions (Ctrl+Space) Подбор имени на базе искусственного интеллекта (CTRL+ПРОБЕЛ) @@ -67,6 +72,11 @@ Свойство CurrentWindow может быть назначено только один раз. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf index 3ebd606908af6..7f1626305c8a1 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf @@ -32,6 +32,11 @@ Seçim, Etkileşimli Pencere'de yürütülüyor. + + Generating suggestions... + Öneriler oluşturuluyor... + + Get AI-powered rename suggestions (Ctrl+Space) Yapay zeka destekli yeniden adlandırma önerileri alın (Ctrl+Space) @@ -67,6 +72,11 @@ CurrentWindow özelliği yalnızca bir kez atanabilir. + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf index 6d6dbb3d7f0e1..d0104de321777 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf @@ -32,6 +32,11 @@ 在交互窗口中执行所选内容。 + + Generating suggestions... + 正在生成建议... + + Get AI-powered rename suggestions (Ctrl+Space) 获取 AI 支持的重命名建议(Ctrl+空格键) @@ -67,6 +72,11 @@ 只能对 CurrentWindow 属性进行一次赋值。 + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf index 54c10200392fc..5f7eecc96cca7 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf @@ -32,6 +32,11 @@ 正在於互動視窗中執行選取範圍。 + + Generating suggestions... + 正在產生建議... + + Get AI-powered rename suggestions (Ctrl+Space) 取得 AI 支援的重新命名建議 (Ctrl+Space) @@ -67,6 +72,11 @@ CurrentWindow 屬性只能受指派一次。 + + Toggle rename suggestions (Ctrl+Space) + Toggle rename suggestions (Ctrl+Space) + + \ No newline at end of file diff --git a/src/EditorFeatures/Core/Classification/ClassificationTypeDefinitions.cs b/src/EditorFeatures/Core/Classification/ClassificationTypeDefinitions.cs index f7829ff3920bf..201fff367f69d 100644 --- a/src/EditorFeatures/Core/Classification/ClassificationTypeDefinitions.cs +++ b/src/EditorFeatures/Core/Classification/ClassificationTypeDefinitions.cs @@ -48,7 +48,7 @@ internal sealed class ClassificationTypeDefinitions #region User Types - Classes [Export] [Name(ClassificationTypeNames.ClassName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeClassesTypeDefinition; #endregion #region User Types - Records @@ -66,37 +66,37 @@ internal sealed class ClassificationTypeDefinitions #region User Types - Delegates [Export] [Name(ClassificationTypeNames.DelegateName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeDelegatesTypeDefinition; #endregion #region User Types - Enums [Export] [Name(ClassificationTypeNames.EnumName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeEnumsTypeDefinition; #endregion #region User Types - Interfaces [Export] [Name(ClassificationTypeNames.InterfaceName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeInterfacesTypeDefinition; #endregion #region User Types - Modules [Export] [Name(ClassificationTypeNames.ModuleName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeModulesTypeDefinition; #endregion #region User Types - Structures [Export] [Name(ClassificationTypeNames.StructName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeStructuresTypeDefinition; #endregion #region User Types - Type Parameters [Export] [Name(ClassificationTypeNames.TypeParameterName)] - [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + [BaseDefinition(PredefinedClassificationTypeNames.Identifier)] internal readonly ClassificationTypeDefinition UserTypeTypeParametersTypeDefinition; #endregion @@ -423,6 +423,13 @@ internal sealed class ClassificationTypeDefinitions internal readonly ClassificationTypeDefinition ReassignedVariableTypeDefinition; #endregion + #region Obsolete Symbol + [Export] + [Name(ClassificationTypeNames.ObsoleteSymbol)] + [BaseDefinition(PredefinedClassificationTypeNames.FormalLanguage)] + internal readonly ClassificationTypeDefinition ObsoleteSymbolTypeDefinition; + #endregion + #region Static Symbol [Export] [Name(ClassificationTypeNames.StaticSymbol)] diff --git a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index 88b5464cdb662..5566e5a508276 100644 --- a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Tagging; @@ -25,10 +25,9 @@ namespace Microsoft.CodeAnalysis.Classification; internal partial class CopyPasteAndPrintingClassificationBufferTaggerProvider { - private sealed class Tagger : IAccurateTagger, IDisposable + public sealed class Tagger : IAccurateTagger, IDisposable { private readonly CopyPasteAndPrintingClassificationBufferTaggerProvider _owner; - private readonly ITextBuffer _subjectBuffer; private readonly ITaggerEventSource _eventSource; private readonly IGlobalOptionService _globalOptions; @@ -45,7 +44,6 @@ public Tagger( IGlobalOptionService globalOptions) { _owner = owner; - _subjectBuffer = subjectBuffer; _globalOptions = globalOptions; _eventSource = TaggerEventSources.Compose( @@ -88,89 +86,117 @@ private void OnEventSourceChanged(object? sender, TaggerEventArgs _) public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { - _owner._threadingContext.ThrowIfNotOnUIThread(); - // we never return any tags for GetTags. This tagger is only for 'Accurate' scenarios. return []; } + private static IEnumerable> GetIntersectingTags(NormalizedSnapshotSpanCollection spans, TagSpanIntervalTree cachedTags) + => SegmentedListPool>.ComputeList( + static (args, tags) => args.cachedTags.AddIntersectingTagSpans(args.spans, tags), + (cachedTags, spans)); + public IEnumerable> GetAllTags(NormalizedSnapshotSpanCollection spans, CancellationToken cancellationToken) { - _owner._threadingContext.ThrowIfNotOnUIThread(); if (spans.Count == 0) return []; - var firstSpan = spans.First(); - var snapshot = firstSpan.Snapshot; - Debug.Assert(snapshot.TextBuffer == _subjectBuffer); + var snapshot = spans.First().Snapshot; var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) return []; - var classificationService = document.GetLanguageService(); - if (classificationService == null) - return []; - // We want to classify from the start of the first requested span to the end of the // last requested span. var spanToTag = new SnapshotSpan(snapshot, Span.FromBounds(spans.First().Start, spans.Last().End)); - GetCachedInfo(out var cachedTaggedSpan, out var cachedTags); + var (cachedTaggedSpan, cachedTags) = GetCachedInfo(); // We don't need to actually classify if what we're being asked for is a subspan // of the last classification we performed. - var canReuseCache = - cachedTaggedSpan?.Snapshot == snapshot && - cachedTaggedSpan.Value.Contains(spanToTag); + if (cachedTaggedSpan?.Snapshot == snapshot && + cachedTaggedSpan.Value.Contains(spanToTag)) + { + Contract.ThrowIfNull(cachedTags); + return GetIntersectingTags(spans, cachedTags); + } + else + { + return ComputeAndCacheAllTags(spans, snapshot, document, spanToTag, cancellationToken); + } + } + + private IEnumerable> ComputeAndCacheAllTags( + NormalizedSnapshotSpanCollection spans, + ITextSnapshot snapshot, + Document document, + SnapshotSpan spanToTag, + CancellationToken cancellationToken) + { + var classificationService = document.GetRequiredLanguageService(); + + // Our cache is not there, or is out of date. We need to compute the up to date results. + var options = _globalOptions.GetClassificationOptions(document.Project.Language); + + // Final list of tags to produce, containing syntax/semantic/embedded classification tags. + using var _ = SegmentedListPool.GetPooledList>(out var mergedTags); - if (!canReuseCache) + _owner._threadingContext.JoinableTaskFactory.Run(async () => { - // Our cache is not there, or is out of date. We need to compute the up to date results. - var context = new TaggerContext(document, snapshot); - var options = _globalOptions.GetClassificationOptions(document.Project.Language); + // Defer to our helper which will compute syntax/semantic/embedded classifications, properly + // layering them into the final result we return. + await TotalClassificationAggregateTagger.AddTagsAsync( + new NormalizedSnapshotSpanCollection(spanToTag), + mergedTags, + // We should only be asking for a single span when getting the syntactic classifications + GetTaggingFunction(requireSingleSpan: true, (span, buffer) => classificationService.AddSyntacticClassificationsAsync(document, span, buffer, cancellationToken)), + // We should only be asking for a single span when getting the semantic classifications + GetTaggingFunction(requireSingleSpan: true, (span, buffer) => classificationService.AddSemanticClassificationsAsync(document, span, options, buffer, cancellationToken)), + // Note: many string literal spans may be passed in when getting embedded classifications + GetTaggingFunction(requireSingleSpan: false, (span, buffer) => classificationService.AddEmbeddedLanguageClassificationsAsync(document, span, options, buffer, cancellationToken)), + arg: default).ConfigureAwait(false); + }); + + var cachedTags = new TagSpanIntervalTree(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive, mergedTags); - _owner._threadingContext.JoinableTaskFactory.Run(async () => - { - var snapshotSpan = new DocumentSnapshotSpan(document, spanToTag); + lock (_gate) + { + _cachedTaggedSpan = spanToTag; + _cachedTags = cachedTags; + } - // When copying/pasting, ensure we have classifications fully computed for the requested spans - // for both semantic classifications and embedded lang classifications. - await ProduceTagsAsync(context, snapshotSpan, classificationService, options, ClassificationType.Semantic, cancellationToken).ConfigureAwait(false); - await ProduceTagsAsync(context, snapshotSpan, classificationService, options, ClassificationType.EmbeddedLanguage, cancellationToken).ConfigureAwait(false); - }); + return GetIntersectingTags(spans, cachedTags); - cachedTaggedSpan = spanToTag; - cachedTags = new TagSpanIntervalTree(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive, context.TagSpans); + Func>, VoidResult, Task> GetTaggingFunction( + bool requireSingleSpan, Func, Task> addTagsAsync) + { + Contract.ThrowIfTrue(requireSingleSpan && spans.Count != 1, "We should only be asking for a single span"); + return (spans, tempBuffer, _) => AddSpansAsync(spans, tempBuffer, addTagsAsync); + } + + async Task AddSpansAsync( + NormalizedSnapshotSpanCollection spans, + SegmentedList> result, + Func, Task> addAsync) + { + // temp buffer we can use across all our classification calls. Should be cleared between each call. + using var _ = Classifier.GetPooledList(out var tempBuffer); - lock (_gate) + foreach (var span in spans) { - _cachedTaggedSpan = cachedTaggedSpan; - _cachedTags = cachedTags; + tempBuffer.Clear(); + await addAsync(span.Span.ToTextSpan(), tempBuffer).ConfigureAwait(false); + + foreach (var classifiedSpan in tempBuffer) + result.Add(ClassificationUtilities.Convert(_owner._typeMap, snapshot, classifiedSpan)); } } - - return SegmentedListPool.ComputeList( - static (args, tags) => args.cachedTags?.AddIntersectingTagSpans(args.spans, tags), - (cachedTags, spans), - _: (ITagSpan?)null); } - private Task ProduceTagsAsync( - TaggerContext context, DocumentSnapshotSpan snapshotSpan, - IClassificationService classificationService, ClassificationOptions options, ClassificationType type, CancellationToken cancellationToken) - { - return ClassificationUtilities.ProduceTagsAsync( - context, snapshotSpan, classificationService, _owner._typeMap, options, type, cancellationToken); - } - - private void GetCachedInfo(out SnapshotSpan? cachedTaggedSpan, out TagSpanIntervalTree? cachedTags) + private (SnapshotSpan? cachedTaggedSpan, TagSpanIntervalTree? cachedTags) GetCachedInfo() { lock (_gate) - { - cachedTaggedSpan = _cachedTaggedSpan; - cachedTags = _cachedTags; - } + return (_cachedTaggedSpan, _cachedTags); } } } diff --git a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs index 7679a7cb4ac9d..d432e03ee5723 100644 --- a/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs @@ -23,6 +23,9 @@ namespace Microsoft.CodeAnalysis.Classification; /// i.e. if you're printing, you want semantic classification even for code that's not in view. /// The same applies to copy/pasting. /// +/// +/// The returned from can be used on any thread. +/// [Export(typeof(ITaggerProvider))] [TagType(typeof(IClassificationTag))] [ContentType(ContentTypeNames.CSharpContentType)] @@ -40,7 +43,7 @@ internal partial class CopyPasteAndPrintingClassificationBufferTaggerProvider( private readonly ClassificationTypeMap _typeMap = typeMap; private readonly IGlobalOptionService _globalOptions = globalOptions; - public IAccurateTagger? CreateTagger(ITextBuffer buffer) where T : ITag + public Tagger? CreateTagger(ITextBuffer buffer) where T : ITag { _threadingContext.ThrowIfNotOnUIThread(); @@ -51,9 +54,9 @@ internal partial class CopyPasteAndPrintingClassificationBufferTaggerProvider( return null; } - return new Tagger(this, buffer, _asyncListener, _globalOptions) as IAccurateTagger; + return new Tagger(this, buffer, _asyncListener, _globalOptions); } ITagger? ITaggerProvider.CreateTagger(ITextBuffer buffer) - => CreateTagger(buffer); + => CreateTagger(buffer) as IAccurateTagger; } diff --git a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs index 88b1ff86d4d79..275ae3569a345 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs @@ -63,7 +63,8 @@ protected sealed override ITaggerEventSource CreateEventSource(ITextView textVie TaggerEventSources.OnViewSpanChanged(ThreadingContext, textView), TaggerEventSources.OnWorkspaceChanged(subjectBuffer, AsyncListener), TaggerEventSources.OnDocumentActiveContextChanged(subjectBuffer), - TaggerEventSources.OnGlobalOptionChanged(_globalOptions, ClassificationOptionsStorage.ClassifyReassignedVariables)); + TaggerEventSources.OnGlobalOptionChanged(_globalOptions, ClassificationOptionsStorage.ClassifyReassignedVariables), + TaggerEventSources.OnGlobalOptionChanged(_globalOptions, ClassificationOptionsStorage.ClassifyObsoleteSymbols)); } protected sealed override Task ProduceTagsAsync( diff --git a/src/EditorFeatures/Core/Classification/TotalClassificationTaggerProvider.cs b/src/EditorFeatures/Core/Classification/TotalClassificationTaggerProvider.cs index ed3aab5395493..89800c1090014 100644 --- a/src/EditorFeatures/Core/Classification/TotalClassificationTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/TotalClassificationTaggerProvider.cs @@ -3,10 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -21,6 +21,7 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification; @@ -68,166 +69,207 @@ internal sealed class TotalClassificationTaggerProvider( return new TotalClassificationAggregateTagger(syntacticTagger, semanticTagger, embeddedTagger); } +} + +internal sealed class TotalClassificationAggregateTagger( + EfficientTagger syntacticTagger, + EfficientTagger semanticTagger, + EfficientTagger embeddedTagger) + : AbstractAggregateTagger([syntacticTagger, semanticTagger, embeddedTagger]) +{ + private static readonly Comparison> s_spanComparison = static (s1, s2) => s1.Span.Start - s2.Span.Start; - internal sealed class TotalClassificationAggregateTagger( - EfficientTagger syntacticTagger, - EfficientTagger semanticTagger, - EfficientTagger embeddedTagger) - : AbstractAggregateTagger(ImmutableArray.Create(syntacticTagger, semanticTagger, embeddedTagger)) + public override void AddTags(NormalizedSnapshotSpanCollection spans, SegmentedList> totalTags) { - private static readonly Comparison> s_spanComparison = static (s1, s2) => s1.Span.Start - s2.Span.Start; + // Everything we pass in is synchronous, so we should immediately get a completed task back out. + AddTagsAsync( + spans, + totalTags, + addSyntacticSpansAsync: static (spans, tags, arg) => + { + arg.syntacticTagger.AddTags(spans, tags); + return Task.CompletedTask; + }, + addSemanticSpansAsync: static (spans, tags, arg) => + { + arg.semanticTagger.AddTags(spans, tags); + return Task.CompletedTask; + }, + addEmbeddedSpansAsync: static (spans, tags, arg) => + { + arg.embeddedTagger.AddTags(spans, tags); + return Task.CompletedTask; + }, + (syntacticTagger, semanticTagger, embeddedTagger)).VerifyCompleted(); + } - public override void AddTags(NormalizedSnapshotSpanCollection spans, SegmentedList> totalTags) - { - // First, get all the syntactic tags. While they are generally overridden by semantic tags (since semantics - // allows us to understand better what things like identifiers mean), they do take precedence for certain - // tags like 'Comments' and 'Excluded Code'. In those cases we want the classification to 'snap' instantly to - // the syntactic state, and we do not want things like semantic classifications showing up over that. + public static async Task AddTagsAsync( + NormalizedSnapshotSpanCollection spans, + SegmentedList> totalTags, + Func>, TArg, Task> addSyntacticSpansAsync, + Func>, TArg, Task> addSemanticSpansAsync, + Func>, TArg, Task> addEmbeddedSpansAsync, + TArg arg) + { + // First, get all the syntactic tags. While they are generally overridden by semantic tags (since semantics + // allows us to understand better what things like identifiers mean), they do take precedence for certain + // tags like 'Comments' and 'Excluded Code'. In those cases we want the classification to 'snap' instantly to + // the syntactic state, and we do not want things like semantic classifications showing up over that. - using var _1 = SegmentedListPool.GetPooledList>(out var stringLiterals); - using var _2 = SegmentedListPool.GetPooledList>(out var syntacticSpans); - using var _3 = SegmentedListPool.GetPooledList>(out var semanticSpans); + using var _1 = SegmentedListPool.GetPooledList>(out var stringLiterals); + using var _2 = SegmentedListPool.GetPooledList>(out var syntacticSpans); + using var _3 = SegmentedListPool.GetPooledList>(out var semanticSpans); - syntacticTagger.AddTags(spans, syntacticSpans); - semanticTagger.AddTags(spans, semanticSpans); + await addSyntacticSpansAsync(spans, syntacticSpans, arg).ConfigureAwait(false); + await addSemanticSpansAsync(spans, semanticSpans, arg).ConfigureAwait(false); - syntacticSpans.Sort(s_spanComparison); - semanticSpans.Sort(s_spanComparison); + syntacticSpans.Sort(s_spanComparison); + semanticSpans.Sort(s_spanComparison); - using var syntacticEnumerator = syntacticSpans.GetEnumerator(); - using var semanticEnumerator = semanticSpans.GetEnumerator(); + using var syntacticEnumerator = syntacticSpans.GetEnumerator(); + using var semanticEnumerator = semanticSpans.GetEnumerator(); - var currentSyntactic = GetNextSyntacticSpan(); - var currentSemantic = GetNextSemanticSpan(); + var currentSyntactic = GetNextSyntacticSpan(); + var currentSemantic = GetNextSemanticSpan(); - while (currentSyntactic != null && currentSemantic != null) + while (currentSyntactic != null && currentSemantic != null) + { + // If both the syntactic and semantic tags are for the same span, and the semantic tag is more specific, + // then just prefer that one (and eschew the syntactic one). Semantics is more accurate, but often will + // produce these accurate tags more slowly than the syntactic classifier. This allows the syntactic + // classifier to produce an initial result, which the semantic classifier can refine. + if (currentSyntactic.Span == currentSemantic.Span && + currentSemantic.Tag.ClassificationType.IsOfType(currentSyntactic.Tag.ClassificationType.Classification)) { - // as long as we see semantic spans before the next syntactic one, keep adding them. - if (currentSemantic.Span.Start <= currentSyntactic.Span.Start) - { - totalTags.Add(currentSemantic); - currentSemantic = GetNextSemanticSpan(); - } - else - { - // We're on a syntactic span before the next semantic one. - - // If it's a comment or excluded code, then we want to ignore every semantic classification that - // potentially overlaps with it so that semantic classifications don't show up *on top of* them. We - // want commenting out code to feel like' it instantly snaps to that state. - if (TryProcessCommentOrExcludedCode()) - continue; - - // If we have a string literal of some sort add it to the list to be processed later. We'll want to - // compute embedded classifications for them, and have those classifications override the string - // literals. - if (TryProcessSyntacticStringLiteral()) - continue; - - // Normal case. Just add the syntactic span and continue. - totalTags.Add(currentSyntactic); - currentSyntactic = GetNextSyntacticSpan(); - } + totalTags.Add(currentSemantic); + currentSyntactic = GetNextSyntacticSpan(); + currentSemantic = GetNextSemanticSpan(); } - - // Add any remaining semantic spans following the syntactic ones. - while (currentSemantic != null) + else if (currentSemantic.Span.Start <= currentSyntactic.Span.Start) { + // as long as we see semantic spans before the next syntactic one, keep adding them. totalTags.Add(currentSemantic); currentSemantic = GetNextSemanticSpan(); } - - // Add any remaining syntactic spans following the semantic ones. - while (currentSyntactic != null) + else { - // don't have to worry about comments/excluded code since there are no semantic tags we want to override. + // We're on a syntactic span before the next semantic one. + + // If it's a comment or excluded code, then we want to ignore every semantic classification that + // potentially overlaps with it so that semantic classifications don't show up *on top of* them. We + // want commenting out code to feel like' it instantly snaps to that state. + if (TryProcessCommentOrExcludedCode()) + continue; + + // If we have a string literal of some sort add it to the list to be processed later. We'll want to + // compute embedded classifications for them, and have those classifications override the string + // literals. if (TryProcessSyntacticStringLiteral()) continue; + // Normal case. Just add the syntactic span and continue. totalTags.Add(currentSyntactic); currentSyntactic = GetNextSyntacticSpan(); } + } - // We've added almost all the syntactic and semantic tags (properly skipping any semantic tags that are - // overridden by comments or excluded code). All that remains is adding back the string literals we - // skipped. However, when we do so, we'll see if those string literals themselves should be overridden - // by any embedded classifications. - AddEmbeddedClassifications(); + // Add any remaining semantic spans following the syntactic ones. + while (currentSemantic != null) + { + totalTags.Add(currentSemantic); + currentSemantic = GetNextSemanticSpan(); + } - return; + // Add any remaining syntactic spans following the semantic ones. + while (currentSyntactic != null) + { + // don't have to worry about comments/excluded code since there are no semantic tags we want to override. + if (TryProcessSyntacticStringLiteral()) + continue; - bool TryProcessSyntacticStringLiteral() - { - if (currentSyntactic.Tag.ClassificationType.Classification is not ClassificationTypeNames.StringLiteral and not ClassificationTypeNames.VerbatimStringLiteral) - return false; + totalTags.Add(currentSyntactic); + currentSyntactic = GetNextSyntacticSpan(); + } - stringLiterals.Add(currentSyntactic); - currentSyntactic = GetNextSyntacticSpan(); - return true; - } + // We've added almost all the syntactic and semantic tags (properly skipping any semantic tags that are + // overridden by comments or excluded code). All that remains is adding back the string literals we + // skipped. However, when we do so, we'll see if those string literals themselves should be overridden + // by any embedded classifications. + await AddEmbeddedClassificationsAsync().ConfigureAwait(false); - bool TryProcessCommentOrExcludedCode() - { - if (currentSyntactic.Tag.ClassificationType.Classification is not ClassificationTypeNames.Comment and not ClassificationTypeNames.ExcludedCode) - return false; + return; - // Keep skipping semantic tags that overlaps with this syntactic tag. - while (currentSemantic != null && currentSemantic.Span.OverlapsWith(currentSyntactic.Span.Span)) - currentSemantic = GetNextSemanticSpan(); + bool TryProcessSyntacticStringLiteral() + { + if (currentSyntactic.Tag.ClassificationType.Classification is not ClassificationTypeNames.StringLiteral and not ClassificationTypeNames.VerbatimStringLiteral) + return false; - // now add that syntactic span. - totalTags.Add(currentSyntactic); - currentSyntactic = GetNextSyntacticSpan(); - return true; - } + stringLiterals.Add(currentSyntactic); + currentSyntactic = GetNextSyntacticSpan(); + return true; + } + + bool TryProcessCommentOrExcludedCode() + { + if (currentSyntactic.Tag.ClassificationType.Classification is not ClassificationTypeNames.Comment and not ClassificationTypeNames.ExcludedCode) + return false; + + // Keep skipping semantic tags that overlaps with this syntactic tag. + while (currentSemantic != null && currentSemantic.Span.OverlapsWith(currentSyntactic.Span.Span)) + currentSemantic = GetNextSemanticSpan(); + + // now add that syntactic span. + totalTags.Add(currentSyntactic); + currentSyntactic = GetNextSyntacticSpan(); + return true; + } + + async Task AddEmbeddedClassificationsAsync() + { + // nothing to do if we didn't run into any string literals. + if (stringLiterals.Count == 0) + return; + + // Only need to ask for the spans that overlapped the string literals. + using var _1 = SegmentedListPool.GetPooledList>(out var embeddedClassifications); - void AddEmbeddedClassifications() + var stringLiteralSpansFull = new NormalizedSnapshotSpanCollection(stringLiterals.Select(s => s.Span)); + + // The spans of the string literal itself may be far off screen. Intersect the string literal spans + // with the view spans to get the actual spans we want to classify. + var stringLiteralSpans = NormalizedSnapshotSpanCollection.Intersection(stringLiteralSpansFull, spans); + + await addEmbeddedSpansAsync(stringLiteralSpans, embeddedClassifications, arg).ConfigureAwait(false); + + // Nothing complex to do if we got no embedded classifications back. Just add in all the string + // classifications, untouched. + if (embeddedClassifications.Count == 0) { - // nothing to do if we didn't run into any string literals. - if (stringLiterals.Count == 0) - return; - - // Only need to ask for the spans that overlapped the string literals. - using var _1 = SegmentedListPool.GetPooledList>(out var embeddedClassifications); - - var stringLiteralSpansFull = new NormalizedSnapshotSpanCollection(stringLiterals.Select(s => s.Span)); - - // The spans of the string literal itself may be far off screen. Intersect the string literal spans - // with the view spans to get the actual spans we want to classify. - var stringLiteralSpans = NormalizedSnapshotSpanCollection.Intersection(stringLiteralSpansFull, spans); - - embeddedTagger.AddTags(stringLiteralSpans, embeddedClassifications); - - // Nothing complex to do if we got no embedded classifications back. Just add in all the string - // classifications, untouched. - if (embeddedClassifications.Count == 0) - { - totalTags.AddRange(stringLiterals); - return; - } - - // ClassifierHelper.MergeParts requires these to be sorted. - stringLiterals.Sort(s_spanComparison); - embeddedClassifications.Sort(s_spanComparison); - - // Call into the helper to merge the string literals and embedded classifications into the final result. - // The helper will add all the embedded classifications first, then add string literal classifications - // in the the space between the embedded classifications that were originally classified as a string - // literal. - ClassifierHelper.MergeParts, ClassificationTagSpanIntervalIntrospector>( - stringLiterals, - embeddedClassifications, - totalTags, - static tag => tag.Span.Span.ToTextSpan(), - static (original, final) => new TagSpan(new SnapshotSpan(original.Span.Snapshot, final.ToSpan()), original.Tag)); + totalTags.AddRange(stringLiterals); + return; } - ITagSpan? GetNextSyntacticSpan() - => syntacticEnumerator.MoveNext() ? syntacticEnumerator.Current : null; - - ITagSpan? GetNextSemanticSpan() - => semanticEnumerator.MoveNext() ? semanticEnumerator.Current : null; + // ClassifierHelper.MergeParts requires these to be sorted. + stringLiterals.Sort(s_spanComparison); + embeddedClassifications.Sort(s_spanComparison); + + // Call into the helper to merge the string literals and embedded classifications into the final result. + // The helper will add all the embedded classifications first, then add string literal classifications + // in the the space between the embedded classifications that were originally classified as a string + // literal. + ClassifierHelper.MergeParts, ClassificationTagSpanIntervalIntrospector>( + stringLiterals, + embeddedClassifications, + totalTags, + static tag => tag.Span.Span.ToTextSpan(), + static (original, final) => new TagSpan(new SnapshotSpan(original.Span.Snapshot, final.ToSpan()), original.Tag)); } + + ITagSpan? GetNextSyntacticSpan() + => syntacticEnumerator.MoveNext() ? syntacticEnumerator.Current : null; + + ITagSpan? GetNextSemanticSpan() + => semanticEnumerator.MoveNext() ? semanticEnumerator.Current : null; } private readonly struct ClassificationTagSpanIntervalIntrospector : IIntervalIntrospector> diff --git a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs index 1ec2d03fbfd0b..7468e5dd43fa3 100644 --- a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs +++ b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs @@ -84,8 +84,7 @@ protected override async Task ProduceTagsAsync(TaggerContext con var prompts = await service.GetAvailablePromptTitlesAsync(document, cancellationToken).ConfigureAwait(false); if (prompts.Length > 0) { - // Invoke analysis call into the Copilot service for the containing method's span. - await service.AnalyzeDocumentAsync(document, new(spanToTag.SnapshotSpan.Start, 0), prompts[0], cancellationToken).ConfigureAwait(false); + await service.AnalyzeDocumentAsync(document, spanToTag.SnapshotSpan.Span.ToTextSpan(), prompts[0], cancellationToken).ConfigureAwait(false); } } } diff --git a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs index 3ba759484c71e..528a64cbacb82 100644 --- a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingService.cs @@ -259,7 +259,7 @@ private static ImmutableArray UpdateTrackingSpans( var newSpan = newSpans[i]; Contract.ThrowIfFalse(oldSpan.Flags == newSpan.Flags); - Contract.ThrowIfFalse(oldSpan.Ordinal == newSpan.Ordinal); + Contract.ThrowIfFalse(oldSpan.Id == newSpan.Id); var newTextSpan = snapshot.GetTextSpan(newSpan.LineSpan).ToSpan(); if (oldSpan.Span.GetSpan(snapshot).Span != newTextSpan) @@ -272,7 +272,7 @@ private static ImmutableArray UpdateTrackingSpans( lazyBuilder[i] = new ActiveStatementTrackingSpan( snapshot.CreateTrackingSpan(newTextSpan, SpanTrackingMode.EdgeExclusive), - newSpan.Ordinal, + newSpan.Id, newSpan.Flags, newSpan.UnmappedDocumentId); } @@ -316,7 +316,7 @@ public async ValueTask> GetSpansAsync(Soluti var snapshot = sourceText.FindCorrespondingEditorTextSnapshot(); if (snapshot != null && snapshot.TextBuffer == documentSpans.First().Span.TextBuffer) { - return documentSpans.SelectAsArray(s => new ActiveStatementSpan(s.Ordinal, s.Span.GetSpan(snapshot).ToLinePositionSpan(), s.Flags, s.UnmappedDocumentId)); + return documentSpans.SelectAsArray(s => new ActiveStatementSpan(s.Id, s.Span.GetSpan(snapshot).ToLinePositionSpan(), s.Flags, s.UnmappedDocumentId)); } } } diff --git a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingSpan.cs b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingSpan.cs index 4c950bf180af3..5aec5a7c951c8 100644 --- a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingSpan.cs +++ b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTrackingSpan.cs @@ -8,10 +8,10 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; -internal readonly struct ActiveStatementTrackingSpan(ITrackingSpan trackingSpan, int ordinal, ActiveStatementFlags flags, DocumentId? unmappedDocumentId) +internal readonly struct ActiveStatementTrackingSpan(ITrackingSpan trackingSpan, ActiveStatementId id, ActiveStatementFlags flags, DocumentId? unmappedDocumentId) { public readonly ITrackingSpan Span = trackingSpan; - public readonly int Ordinal = ordinal; + public readonly ActiveStatementId Id = id; public readonly ActiveStatementFlags Flags = flags; public readonly DocumentId? UnmappedDocumentId = unmappedDocumentId; @@ -21,5 +21,5 @@ internal readonly struct ActiveStatementTrackingSpan(ITrackingSpan trackingSpan, public bool IsLeaf => (Flags & ActiveStatementFlags.LeafFrame) != 0; public static ActiveStatementTrackingSpan Create(ITextSnapshot snapshot, ActiveStatementSpan span) - => new(snapshot.CreateTrackingSpan(snapshot.GetTextSpan(span.LineSpan).ToSpan(), SpanTrackingMode.EdgeExclusive), span.Ordinal, span.Flags, span.UnmappedDocumentId); + => new(snapshot.CreateTrackingSpan(snapshot.GetTextSpan(span.LineSpan).ToSpan(), SpanTrackingMode.EdgeExclusive), span.Id, span.Flags, span.UnmappedDocumentId); } diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 10a8aca886b7b..3d18cd9d0eb3c 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs new file mode 100644 index 0000000000000..f4071f8137054 --- /dev/null +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageServiceBridge.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Debugger.Contracts.HotReload; + +namespace Microsoft.CodeAnalysis.EditAndContinue; + +/// +/// Exposes as a brokered service. +/// TODO (https://github.com/dotnet/roslyn/issues/72713): +/// Once debugger is updated to use the brokered service, this class should be removed and should be exported directly. +/// +internal sealed partial class ManagedEditAndContinueLanguageServiceBridge(EditAndContinueLanguageService service) : IManagedHotReloadLanguageService +{ + public ValueTask StartSessionAsync(CancellationToken cancellationToken) + => service.StartSessionAsync(cancellationToken); + + public ValueTask EndSessionAsync(CancellationToken cancellationToken) + => service.EndSessionAsync(cancellationToken); + + public ValueTask EnterBreakStateAsync(CancellationToken cancellationToken) + => service.EnterBreakStateAsync(cancellationToken); + + public ValueTask ExitBreakStateAsync(CancellationToken cancellationToken) + => service.ExitBreakStateAsync(cancellationToken); + + public ValueTask OnCapabilitiesChangedAsync(CancellationToken cancellationToken) + => service.OnCapabilitiesChangedAsync(cancellationToken); + + public async ValueTask GetUpdatesAsync(CancellationToken cancellationToken) + => (await service.GetUpdatesAsync(cancellationToken).ConfigureAwait(false)); + + public ValueTask CommitUpdatesAsync(CancellationToken cancellationToken) + => service.CommitUpdatesAsync(cancellationToken); + + public ValueTask DiscardUpdatesAsync(CancellationToken cancellationToken) + => service.DiscardUpdatesAsync(cancellationToken); + + public ValueTask HasChangesAsync(string? sourceFilePath, CancellationToken cancellationToken) + => service.HasChangesAsync(sourceFilePath, cancellationToken); +} + diff --git a/src/VisualStudio/Core/Def/ProjectSystem/TextEditApplication.cs b/src/EditorFeatures/Core/Editor/TextEditApplication.cs similarity index 91% rename from src/VisualStudio/Core/Def/ProjectSystem/TextEditApplication.cs rename to src/EditorFeatures/Core/Editor/TextEditApplication.cs index 9d06211e86f5b..c4bf5575e8f83 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/TextEditApplication.cs +++ b/src/EditorFeatures/Core/Editor/TextEditApplication.cs @@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; +namespace Microsoft.CodeAnalysis.Editor; internal static class TextEditApplication { @@ -31,7 +31,7 @@ public static void UpdateText(ImmutableArray textChanges, ITextBuffe private static void UpdateText(ImmutableArray textChanges, ITextBuffer buffer, ITextSnapshot oldSnapshot, SourceText oldText, EditOptions options) { using var edit = buffer.CreateEdit(options, reiteratedVersionNumber: null, editTag: null); - if (CodeAnalysis.Workspace.TryGetWorkspace(oldText.Container, out var workspace)) + if (Workspace.TryGetWorkspace(oldText.Container, out var workspace)) { var undoService = workspace.Services.GetRequiredService(); undoService.BeginUndoTransaction(oldSnapshot); diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.resx b/src/EditorFeatures/Core/EditorFeaturesResources.resx index 6b30388240e00..922e026e9cfb2 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.resx +++ b/src/EditorFeatures/Core/EditorFeaturesResources.resx @@ -547,6 +547,9 @@ Do you want to proceed? Cancel + + Run + Changes @@ -923,4 +926,7 @@ Do you want to proceed? Roslyn Test Code Markup + + Obsolete symbol + \ No newline at end of file diff --git a/src/EditorFeatures/Core/ExternalAccess/UnitTesting/Api/UnitTestingGlobalOptions.cs b/src/EditorFeatures/Core/ExternalAccess/UnitTesting/Api/UnitTestingGlobalOptions.cs index 88602125ec598..105761cd9b6bb 100644 --- a/src/EditorFeatures/Core/ExternalAccess/UnitTesting/Api/UnitTestingGlobalOptions.cs +++ b/src/EditorFeatures/Core/ExternalAccess/UnitTesting/Api/UnitTestingGlobalOptions.cs @@ -5,18 +5,15 @@ using System; using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Remote; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; [Export(typeof(UnitTestingGlobalOptions)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class UnitTestingGlobalOptions(IGlobalOptionService globalOptions) +internal sealed class UnitTestingGlobalOptions() { - private readonly IGlobalOptionService _globalOptions = globalOptions; - - public bool IsServiceHubProcessCoreClr - => _globalOptions.GetOption(RemoteHostOptionsStorage.OOPCoreClr); +#pragma warning disable CA1822 // Mark members as static + public bool IsServiceHubProcessCoreClr => true; +#pragma warning restore CA1822 // Mark members as static } diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs deleted file mode 100644 index 5b9864deafe73..0000000000000 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Composition; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; - -[Export(typeof(IVSTypeScriptDiagnosticService)), Shared] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class VSTypeScriptDiagnosticService(IDiagnosticService service) : IVSTypeScriptDiagnosticService -{ - private readonly IDiagnosticService _service = service; - - public Task> GetPushDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) - { - // This type is only for push diagnostics, which is now no longer how any of our diagnostic systems work. So - // this just returns nothing. - return SpecializedTasks.EmptyImmutableArray(); - } - - [Obsolete] - public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action action) - => new EventHandlerWrapper(_service, action); - - public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action> action) - => new EventHandlerWrapper(_service, action); - - private sealed class EventHandlerWrapper : IDisposable - { - private readonly IDiagnosticService _service; - private readonly EventHandler> _handler; - - [Obsolete] - internal EventHandlerWrapper(IDiagnosticService service, Action action) - { - _service = service; - _handler = (sender, argsCollection) => - { - foreach (var args in argsCollection) - action(new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args)); - }; - _service.DiagnosticsUpdated += _handler; - } - - internal EventHandlerWrapper(IDiagnosticService service, Action> action) - { - _service = service; - _handler = (sender, argsCollection) => - { - action(ImmutableArray.CreateRange(argsCollection, static args => new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args))); - }; - _service.DiagnosticsUpdated += _handler; - } - - public void Dispose() - { - _service.DiagnosticsUpdated -= _handler; - } - } -} diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameUIOptionsStorage.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameUIOptionsStorage.cs index 14629a946f40b..4e49a010b802e 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameUIOptionsStorage.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameUIOptionsStorage.cs @@ -10,4 +10,6 @@ internal sealed class InlineRenameUIOptionsStorage { public static readonly Option2 UseInlineAdornment = new("dotnet_rename_use_inline_adornment", defaultValue: true); public static readonly Option2 CollapseUI = new("dotnet_collapse_inline_rename_ui", defaultValue: false); + public static readonly Option2 CollapseSuggestionsPanel = new("dotnet_collapse_suggestions_in_inline_rename_ui", defaultValue: false); + public static readonly Option2 GetSuggestionsAutomatically = new("dotnet_rename_get_suggestions_automatically", defaultValue: false); } diff --git a/src/EditorFeatures/Core/Remote/RemoteHostOptionsStorage.cs b/src/EditorFeatures/Core/Remote/RemoteHostOptionsStorage.cs index b791ccb4817f2..743e4d23e9a76 100644 --- a/src/EditorFeatures/Core/Remote/RemoteHostOptionsStorage.cs +++ b/src/EditorFeatures/Core/Remote/RemoteHostOptionsStorage.cs @@ -11,8 +11,5 @@ internal sealed class RemoteHostOptionsStorage // use 64bit OOP public static readonly Option2 OOP64Bit = new("dotnet_code_analysis_in_separate_process", defaultValue: true); - // use coreclr host for OOP - public static readonly Option2 OOPCoreClr = new("dotnet_enable_core_clr_in_code_analysis_process", defaultValue: true); - public static readonly Option2 OOPServerGCFeatureFlag = new("dotnet_enable_server_garbage_collection_in_code_analysis_process", defaultValue: false); } diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 12dd252c7bec2..92187c42e3748 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -60,9 +60,6 @@ public SolutionChecksumUpdater( listener, shutdownToken); - // Use an equality comparer here as we will commonly get lots of change notifications that will all be - // associated with the same cancellation token controlling that batch of work. No need to enqueue the same - // token a huge number of times when we only need the single value of it when doing the work. _synchronizeWorkspaceQueue = new AsyncBatchingWorkQueue( DelayTimeSpan.NearImmediate, SynchronizePrimaryWorkspaceAsync, @@ -149,10 +146,9 @@ private async ValueTask SynchronizePrimaryWorkspaceAsync(CancellationToken cance using (Logger.LogBlock(FunctionId.SolutionChecksumUpdater_SynchronizePrimaryWorkspace, cancellationToken)) { - var workspaceVersion = solution.WorkspaceVersion; await client.TryInvokeAsync( solution, - (service, solution, cancellationToken) => service.SynchronizePrimaryWorkspaceAsync(solution, workspaceVersion, cancellationToken), + (service, solution, cancellationToken) => service.SynchronizePrimaryWorkspaceAsync(solution, cancellationToken), cancellationToken).ConfigureAwait(false); } } diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs index 3691c669f2017..1bf2cf11cc79a 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.StateMachine.cs @@ -240,11 +240,7 @@ public bool ClearVisibleTrackingSession() // to trigger the diagnostic system to reanalyze, so we trigger it // manually. - _diagnosticAnalyzerService?.Reanalyze( - document.Project.Solution.Workspace, - projectIds: null, - documentIds: SpecializedCollections.SingletonEnumerable(document.Id), - highPriority: true); + _diagnosticAnalyzerService?.RequestDiagnosticRefresh(); } // Disallow the existing TrackingSession from triggering IdentifierFound. diff --git a/src/EditorFeatures/Core/SemanticSearch/SemanticSearchEditorWorkspace.cs b/src/EditorFeatures/Core/SemanticSearch/SemanticSearchEditorWorkspace.cs new file mode 100644 index 0000000000000..d099a979b91b4 --- /dev/null +++ b/src/EditorFeatures/Core/SemanticSearch/SemanticSearchEditorWorkspace.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more 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.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal sealed class SemanticSearchEditorWorkspace( + HostServices services, + SemanticSearchProjectConfiguration config, + IThreadingContext threadingContext, + IAsynchronousOperationListenerProvider listenerProvider) + : SemanticSearchWorkspace(services, config) +{ + private readonly IAsynchronousOperationListener _asyncListener = listenerProvider.GetListener(FeatureAttribute.SemanticSearch); + + private ITextBuffer? _queryTextBuffer; + private DocumentId? _queryDocumentId; + + public async Task OpenQueryDocumentAsync(ITextBuffer buffer, CancellationToken cancellationToken) + { + _queryTextBuffer = buffer; + + // initialize solution with default query, unless it has already been initialized: + var queryDocument = await UpdateQueryDocumentAsync(query: null, cancellationToken).ConfigureAwait(false); + + _queryDocumentId = queryDocument.Id; + + OnDocumentOpened(queryDocument.Id, buffer.AsTextContainer()); + } + + /// + /// Used by code actions through . + /// + protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText) + { + if (documentId == _queryDocumentId) + { + ApplyQueryDocumentTextChanged(newText); + } + } + + protected override void ApplyQueryDocumentTextChanged(SourceText newText) + { + Contract.ThrowIfNull(_queryTextBuffer); + + // update the buffer on UI thread: + + var completionToken = _asyncListener.BeginAsyncOperation(nameof(SemanticSearchEditorWorkspace) + "." + nameof(ApplyQueryDocumentTextChanged)); + _ = UpdateTextAsync().ReportNonFatalErrorAsync().CompletesAsyncOperation(completionToken); + + async Task UpdateTextAsync() + { + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None); + TextEditApplication.UpdateText(newText, _queryTextBuffer, EditOptions.DefaultMinimalChange); + } + } +} diff --git a/src/EditorFeatures/Core/SemanticSearch/SemanticSeatchTextBufferSupportsFeatureService.cs b/src/EditorFeatures/Core/SemanticSearch/SemanticSeatchTextBufferSupportsFeatureService.cs new file mode 100644 index 0000000000000..502a3f88462b5 --- /dev/null +++ b/src/EditorFeatures/Core/SemanticSearch/SemanticSeatchTextBufferSupportsFeatureService.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Editor.Shared; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +[ExportWorkspaceService(typeof(ITextBufferSupportsFeatureService), WorkspaceKind.SemanticSearch), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class SemanticSeatchTextBufferSupportsFeatureService() : ITextBufferSupportsFeatureService +{ + public bool SupportsCodeFixes(ITextBuffer textBuffer) + => true; + + public bool SupportsRefactorings(ITextBuffer textBuffer) + => true; + + public bool SupportsRename(ITextBuffer textBuffer) + => true; + + public bool SupportsNavigationToAnyPosition(ITextBuffer textBuffer) + => true; +} diff --git a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs index 8ec9a5537670b..fa6fae448e9e2 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/Utilities/TagSpanIntervalTree.cs @@ -52,10 +52,9 @@ public bool HasSpanThatContains(SnapshotPoint point) } public IList> GetIntersectingSpans(SnapshotSpan snapshotSpan) - => SegmentedListPool.ComputeList( + => SegmentedListPool>.ComputeList( static (args, tags) => args.@this.AppendIntersectingSpansInSortedOrder(args.snapshotSpan, tags), - (@this: this, snapshotSpan), - _: (ITagSpan?)null); + (@this: this, snapshotSpan)); /// /// Gets all the spans that intersect with in sorted order and adds them to diff --git a/src/EditorFeatures/Core/Tagging/EfficientTagger.cs b/src/EditorFeatures/Core/Tagging/EfficientTagger.cs index 4eaa21b7d1fab..2b73738b90f79 100644 --- a/src/EditorFeatures/Core/Tagging/EfficientTagger.cs +++ b/src/EditorFeatures/Core/Tagging/EfficientTagger.cs @@ -30,10 +30,9 @@ internal abstract class EfficientTagger : ITagger, IDisposable where /// Default impl of the core interface. Forces an allocation. /// public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) - => SegmentedListPool.ComputeList( + => SegmentedListPool>.ComputeList( static (args, tags) => args.@this.AddTags(args.spans, tags), - (@this: this, spans), - _: (ITagSpan?)null); + (@this: this, spans)); public virtual event EventHandler? TagsChanged; diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf index 296a70007ea64..230fbdd0b2d86 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf @@ -202,6 +202,11 @@ Ne + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Operátor – přetížení @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Upřesnit pomocí Copilotu @@ -292,6 +297,11 @@ Roslyn – revize testovacího kódu + + Run + Run + + Split comment Dělený komentář diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf index 826dbc3dc0ec4..072730f4d00f3 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf @@ -202,6 +202,11 @@ Nein + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Operator - überladen @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Mit Copilot verfeinern @@ -292,6 +297,11 @@ Roslyn-Testcodemarkup + + Run + Run + + Split comment Kommentar teilen diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf index dd538dd8a5c67..5a78d5ff51fb5 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf @@ -202,6 +202,11 @@ No + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Operador: sobrecargado @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Refinar con Copilot @@ -292,6 +297,11 @@ Marcado de código de prueba de Roslyn + + Run + Run + + Split comment Dividir comentario diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf index e7a49dbd61378..3da06b40397e1 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf @@ -202,6 +202,11 @@ Non + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Opérateur - surchargé @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Affiner en utilisant Copilot @@ -292,6 +297,11 @@ Balisage du code de test Roslyn + + Run + Run + + Split comment Diviser le commentaire diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf index f1fd95dd838d6..b36a3c74fd020 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf @@ -202,6 +202,11 @@ No + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Operatore - Overload @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Affinare con Copilot @@ -292,6 +297,11 @@ Markup del codice di test di Roslyn + + Run + Run + + Split comment Dividi commento diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf index 972db8cfab01a..b582fb5a423b9 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf @@ -202,6 +202,11 @@ いいえ + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded 演算子 - オーバーロード @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Copilot を使用して絞り込む @@ -292,6 +297,11 @@ Roslyn テスト コード マークアップ + + Run + Run + + Split comment コメントの分割 diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf index 205f935e256fa..8ab379da7864d 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf @@ -202,6 +202,11 @@ 아니요 + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded 연산자 - 오버로드됨 @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Copilot을 사용하여 구체화 @@ -292,6 +297,11 @@ Roslyn 테스트 코드 마크업 + + Run + Run + + Split comment 주석 분할 diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf index 44844805ab126..180d567ab7029 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf @@ -202,6 +202,11 @@ Nie + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Operator — przeciążony @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Uściślanie przy użyciu funkcji Copilot @@ -292,6 +297,11 @@ Adiustacja kodu testowego Roslyn + + Run + Run + + Split comment Podziel komentarz diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf index d4abbbcf97c5f..4d07bb8e799ad 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf @@ -202,6 +202,11 @@ Não + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Operador – Sobrecarregado @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Refinar usando o Copilot @@ -292,6 +297,11 @@ Marcação de Código de Teste Roslyn + + Run + Run + + Split comment Dividir o comentário diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf index e16baca18ecbe..73a7c060f0883 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf @@ -202,6 +202,11 @@ Нет + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded Оператор — перегружен @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Уточнить с помощью Copilot @@ -292,6 +297,11 @@ Разметка тестового кода Roslyn + + Run + Run + + Split comment Разделительный комментарий diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf index da6365927fcd9..8e5a7e551ee83 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf @@ -202,6 +202,11 @@ Hayır + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded İşleç - Aşırı Yüklenmiş @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + Copilot kullanarak geliştir @@ -292,6 +297,11 @@ Roslyn Test Kodu İşaretlemesi + + Run + Run + + Split comment Açıklamayı böl diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf index 877b5031ed46b..b55d4aa19134d 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf @@ -202,6 +202,11 @@ + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded 运算符-重载 @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + 使用 Copilot 优化 @@ -292,6 +297,11 @@ Roslyn 测试代码标记 + + Run + Run + + Split comment 拆分注释 diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf index 94cf68abd76d5..fe000bb08c980 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf @@ -202,6 +202,11 @@ + + Obsolete symbol + Obsolete symbol + + Operator - Overloaded 運算子 - 多載 @@ -259,7 +264,7 @@ Refine using Copilot - Refine using Copilot + 使用 Copilot 縮小搜尋範圍 @@ -292,6 +297,11 @@ Roslyn 測試程式碼標記 + + Run + Run + + Split comment 分割註解 diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index c4c24f20e54ad..ab76c5b379b92 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -155,9 +155,7 @@ private protected virtual IDocumentServiceProvider GetDocumentServiceProvider() => null; protected virtual TestComposition GetComposition() - => EditorTestCompositions.EditorFeatures - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)); + => EditorTestCompositions.EditorFeatures; protected virtual void InitializeWorkspace(EditorTestWorkspace workspace, TestParameters parameters) { diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs index 23aac48821079..aaf8f45515377 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs @@ -27,9 +27,7 @@ public abstract partial class AbstractUserDiagnosticTest { // TODO: IInlineRenameService requires WPF (https://github.com/dotnet/roslyn/issues/46153) private static readonly TestComposition s_composition = EditorTestCompositions.EditorFeaturesWpf - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) .AddParts( - typeof(MockDiagnosticUpdateSourceRegistrationService), typeof(TestGenerateTypeOptionsService), typeof(TestProjectManagementService)); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs index dc323d6b78d01..2f8d8892420d4 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs @@ -33,9 +33,7 @@ public abstract class AbstractMoveTypeTest : AbstractCodeActionTest // TODO: Requires WPF due to IInlineRenameService dependency (https://github.com/dotnet/roslyn/issues/46153) protected override TestComposition GetComposition() - => EditorTestCompositions.EditorFeaturesWpf - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)); + => EditorTestCompositions.EditorFeaturesWpf; protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters) => new MoveTypeCodeRefactoringProvider(); diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 3e75fd87eb280..815c9aee54ca7 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -36,9 +36,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeFixes [UseExportProvider] public class CodeFixServiceTests { - private static readonly TestComposition s_compositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)); + private static readonly TestComposition s_compositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures; [Fact] public async Task TestGetFirstDiagnosticWithFixAsync() @@ -49,7 +47,6 @@ public async Task TestGetFirstDiagnosticWithFixAsync() "; using var workspace = TestWorkspace.CreateCSharp(code, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService, openDocuments: true); - Assert.IsType(workspace.GetService()); var diagnosticService = Assert.IsType(workspace.GetService()); var analyzerReference = new TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()); @@ -365,7 +362,6 @@ private static (EditorTestWorkspace workspace, DiagnosticAnalyzerService analyze var analyzerReference = new TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); - Assert.IsType(workspace.GetService()); var diagnosticService = Assert.IsType(workspace.GetService()); var logger = SpecializedCollections.SingletonEnumerable(new Lazy(() => new TestErrorLogger())); var errorLogger = logger.First().Value; @@ -778,7 +774,6 @@ private static async Task> GetNuGetAndVsixCode using var workspace = TestWorkspace.CreateCSharp(code, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService, openDocuments: true); - Assert.IsType(workspace.GetService()); var diagnosticService = Assert.IsType(workspace.GetService()); var logger = SpecializedCollections.SingletonEnumerable(new Lazy(() => workspace.Services.GetRequiredService())); diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index ec4590845f359..1e1e5e0e82eec 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -38,13 +38,9 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics public class DiagnosticAnalyzerServiceTests { private static readonly TestComposition s_featuresCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)) .AddParts(typeof(TestDocumentTrackingService)); - private static readonly TestComposition s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)); + private static readonly TestComposition s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures; private static AdhocWorkspace CreateWorkspace(Type[] additionalParts = null) { @@ -78,23 +74,13 @@ public async Task TestHasSuccessfullyLoadedBeingFalse() var document = GetDocumentFromIncompleteProject(workspace); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); var analyzer = service.CreateIncrementalAnalyzer(workspace); var globalOptions = exportProvider.GetExportedValue(); - // listen to events - // check empty since this could be called to clear up existing diagnostics - service.DiagnosticsUpdated += (s, a) => - { - Assert.All(a, e => Assert.Empty(e.Diagnostics)); - }; - - await analyzer.GetDiagnosticsAsync( - workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, CancellationToken.None); - - // wait for all events to raised - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); + var diagnostics = await analyzer.GetDiagnosticsAsync( + workspace.CurrentSolution, projectId: null, documentId: null, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: false, CancellationToken.None); + Assert.NotEmpty(diagnostics); } [Fact] @@ -202,7 +188,6 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab Assert.True(applied); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); var analyzer = service.CreateIncrementalAnalyzer(workspace); @@ -210,36 +195,27 @@ public async Task TestDisabledByDefaultAnalyzerEnabledWithEditorConfig(bool enab var syntaxDiagnostic = false; var semanticDiagnostic = false; var compilationDiagnostic = false; - service.DiagnosticsUpdated += (s, aCollection) => - { - foreach (var a in aCollection) - { - var diagnostics = a.Diagnostics; - var diagnostic = Assert.Single(diagnostics); - Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity); - - if (diagnostic.Id == DisabledByDefaultAnalyzer.s_syntaxRule.Id) - { - syntaxDiagnostic = true; - } - else if (diagnostic.Id == DisabledByDefaultAnalyzer.s_semanticRule.Id) - { - semanticDiagnostic = true; - } - else if (diagnostic.Id == DisabledByDefaultAnalyzer.s_compilationRule.Id) - { - compilationDiagnostic = true; - } - } - }; // open document workspace.OpenDocument(document.Id); - await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); - // wait for all events to raised - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); + foreach (var diagnostic in diagnostics) + { + if (diagnostic.Id == DisabledByDefaultAnalyzer.s_syntaxRule.Id) + { + syntaxDiagnostic = true; + } + else if (diagnostic.Id == DisabledByDefaultAnalyzer.s_semanticRule.Id) + { + semanticDiagnostic = true; + } + else if (diagnostic.Id == DisabledByDefaultAnalyzer.s_compilationRule.Id) + { + compilationDiagnostic = true; + } + } Assert.Equal(enabledWithEditorconfig, syntaxDiagnostic); Assert.Equal(enabledWithEditorconfig, semanticDiagnostic); @@ -254,169 +230,22 @@ private static async Task TestAnalyzerAsync( { var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var globalOptions = exportProvider.GetExportedValue(); var analyzer = service.CreateIncrementalAnalyzer(workspace); var syntax = false; var semantic = false; - // listen to events - service.DiagnosticsUpdated += (s, aCollection) => - { - foreach (var a in aCollection) - { - var diagnostics = a.Diagnostics; - (syntax, semantic) = resultSetter(syntax, semantic, diagnostics); - } - }; - - await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); + var diagnostics = await analyzer.ForceAnalyzeProjectAsync(document.Project, CancellationToken.None); - // wait for all events to raised - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); + (syntax, semantic) = resultSetter(syntax, semantic, diagnostics); // two should have been called. Assert.Equal(expectedSyntax, syntax); Assert.Equal(expectedSemantic, semantic); } - [Fact] - public async Task TestOpenFileOnlyAnalyzerDiagnostics() - { - using var workspace = CreateWorkspace(); - - var exportProvider = workspace.Services.SolutionServices.ExportProvider; - var globalOptions = exportProvider.GetExportedValue(); - - var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create(new OpenFileOnlyAnalyzer())); - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); - - var project = workspace.AddProject( - ProjectInfo.Create( - ProjectId.CreateNewId(), - VersionStamp.Create(), - "CSharpProject", - "CSharpProject", - LanguageNames.CSharp)); - - var document = workspace.AddDocument(project.Id, "Empty.cs", SourceText.From("")); - - Assert.IsType(exportProvider.GetExportedValue()); - var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = service.CreateIncrementalAnalyzer(workspace); - - // listen to events - service.DiagnosticsUpdated += (s, aCollection) => - { - foreach (var a in aCollection) - { - if (workspace.IsDocumentOpen(a.DocumentId)) - { - var diagnostics = a.Diagnostics; - // check the diagnostics are reported - Assert.Equal(document.Id, a.DocumentId); - Assert.Equal(1, diagnostics.Length); - Assert.Equal(OpenFileOnlyAnalyzer.s_syntaxRule.Id, diagnostics[0].Id); - } - - if (a.DocumentId == document.Id && !workspace.IsDocumentOpen(a.DocumentId)) - { - // check the diagnostics reported are cleared - var diagnostics = a.Diagnostics; - Assert.Equal(0, diagnostics.Length); - } - } - }; - - // open document - workspace.OpenDocument(document.Id); - - // close document - workspace.CloseDocument(document.Id); - - // wait for all events to raised - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); - } - - [Fact] - public async Task TestSynchronizeWithBuild() - { - using var workspace = CreateWorkspace([typeof(NoCompilationLanguageService)]); - - var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create(new NoNameAnalyzer())); - workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); - - var language = NoCompilationConstants.LanguageName; - - var project = workspace.AddProject( - ProjectInfo.Create( - ProjectId.CreateNewId(), - VersionStamp.Create(), - "NoNameProject", - "NoNameProject", - language)); - - var filePath = "NoNameDoc.other"; - var document = workspace.AddDocument( - DocumentInfo.Create( - DocumentId.CreateNewId(project.Id), - "Empty", - loader: TextLoader.From(TextAndVersion.Create(SourceText.From(""), VersionStamp.Create(), filePath)), - filePath: filePath)); - - var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); - var service = Assert.IsType(exportProvider.GetExportedValue()); - var analyzer = service.CreateIncrementalAnalyzer(workspace); - var globalOptions = exportProvider.GetExportedValue(); - - var syntax = false; - - // listen to events - service.DiagnosticsUpdated += (s, aCollection) => - { - foreach (var a in aCollection) - { - var diagnostics = a.Diagnostics; - switch (diagnostics.Length) - { - case 0: - continue; - case 1: - syntax |= diagnostics[0].Id == NoNameAnalyzer.s_syntaxRule.Id; - continue; - default: - AssertEx.Fail("shouldn't reach here"); - continue; - } - } - }; - - // cause analysis - var location = Location.Create(document.FilePath, textSpan: default, lineSpan: default); - var properties = ImmutableDictionary.Empty.Add(WellKnownDiagnosticPropertyNames.Origin, WellKnownDiagnosticTags.Build); - - await service.SynchronizeWithBuildAsync( - workspace, - ImmutableDictionary>.Empty.Add( - document.Project.Id, - ImmutableArray.Create(DiagnosticData.Create(document.Project.Solution, Diagnostic.Create(NoNameAnalyzer.s_syntaxRule, location, properties), document.Project))), - new TaskQueue(service.Listener, TaskScheduler.Default), - onBuildCompleted: true, - CancellationToken.None); - - // wait for all events to raised - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync().ConfigureAwait(false); - - // two should have been called. - Assert.True(syntax); - - // we should reach here without crashing - } - [Fact] public void TestHostAnalyzerOrdering() { @@ -443,7 +272,6 @@ public void TestHostAnalyzerOrdering() "Dummy", LanguageNames.CSharp)); - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); @@ -494,32 +322,11 @@ public async Task TestHostAnalyzerErrorNotLeaking() filePath: "test.cs")})); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var called = false; - service.DiagnosticsUpdated += (s, eCollection) => - { - foreach (var e in eCollection) - { - var diagnostics = e.Diagnostics; - if (diagnostics.Length == 0) - { - continue; - } - - var liveId = (LiveDiagnosticUpdateArgsId)e.Id; - Assert.False(liveId.Analyzer is ProjectDiagnosticAnalyzer); - - called = true; - } - }; - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); - - Assert.True(called); + var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + Assert.NotEmpty(diagnostics); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42353")] @@ -601,33 +408,19 @@ private static AdhocWorkspace CreateWorkspaceWithProjectAndAnalyzer(DiagnosticAn private static async Task TestFullSolutionAnalysisForProjectAsync(AdhocWorkspace workspace, Project project, bool expectAnalyzerExecuted) { var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var globalOptions = exportProvider.GetExportedValue(); - - var called = false; - service.DiagnosticsUpdated += (s, eCollection) => - { - foreach (var e in eCollection) - { - var diagnostics = e.Diagnostics; - if (diagnostics.Length == 0) - { - continue; - } - - var liveId = (LiveDiagnosticUpdateArgsId)e.Id; - Assert.True(liveId.Analyzer is NamedTypeAnalyzer); - - called = true; - } - }; var incrementalAnalyzer = service.CreateIncrementalAnalyzer(project.Solution.Workspace); - await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); + var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - Assert.Equal(expectAnalyzerExecuted, called); + if (expectAnalyzerExecuted) + { + Assert.NotEmpty(diagnostics); + } + else + { + Assert.Empty(diagnostics); + } } [Theory, CombinatorialData] @@ -663,27 +456,18 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool Assert.True(applied); var exportProvider = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(exportProvider.GetExportedValue()); var service = Assert.IsType(exportProvider.GetExportedValue()); - var diagnostics = new ConcurrentSet(); - service.DiagnosticsUpdated += (s, eCollection) => - { - foreach (var e in eCollection) - diagnostics.AddRange(e.Diagnostics); - }; - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); var firstAdditionalDocument = project.AdditionalDocuments.FirstOrDefault(); workspace.OpenAdditionalDocument(firstAdditionalDocument.Id); - await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); + var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); var expectedCount = testMultiple ? 4 : 1; - Assert.Equal(expectedCount, diagnostics.Count); + Assert.Equal(expectedCount, diagnostics.Length); for (var i = 0; i < analyzers.Length; i++) { @@ -697,7 +481,7 @@ internal async Task TestAdditionalFileAnalyzer(bool registerFromInitialize, bool var diagnostic = Assert.Single(applicableDiagnostics); Assert.Equal(diagnosticSpan, diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)); - diagnostics.Remove(diagnostic); + diagnostics = diagnostics.Remove(diagnostic); } } @@ -739,25 +523,9 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS var project = workspace.CurrentSolution.Projects.Single(); var document = project.Documents.Single(); - Assert.IsType(workspace.GetService()); var service = Assert.IsType(workspace.GetService()); var globalOptions = workspace.GetService(); - DiagnosticData diagnostic = null; - service.DiagnosticsUpdated += (s, eCollection) => - { - foreach (var e in eCollection) - { - var diagnostics = e.Diagnostics; - if (diagnostics.Length == 0) - { - continue; - } - - diagnostic = Assert.Single(diagnostics); - } - }; - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); switch (analysisScope) @@ -780,9 +548,9 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS throw ExceptionUtilities.UnexpectedValue(analysisScope); } - await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); + var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostic = diagnostics.SingleOrDefault(); if (includeAnalyzer) { Assert.True(diagnostic != null); @@ -876,21 +644,9 @@ void M() else Assert.IsType(document); - Assert.IsType(workspace.GetService()); var service = Assert.IsType(workspace.GetService()); - var diagnostics = ArrayBuilder.GetInstance(); var text = await document.GetTextAsync(); - service.DiagnosticsUpdated += (s, eCollection) => - { - foreach (var e in eCollection) - { - diagnostics.AddRange( - e.Diagnostics - .Where(d => d.Id == IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId) - .OrderBy(d => d.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))); - } - }; var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); @@ -919,13 +675,17 @@ void M() break; } - await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); + var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + + diagnostics = diagnostics + .Where(d => d.Id == IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId) + .OrderBy(d => d.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text)) + .ToImmutableArray(); var root = await document.GetSyntaxRootAsync(); text = await document.GetTextAsync(); - Assert.Equal(2, diagnostics.Count); + Assert.Equal(2, diagnostics.Length); if (testPragma) { var pragma1 = root.FindTrivia(diagnostics[0].DataLocation.UnmappedFileSpan.GetClampedTextSpan(text).Start).ToString(); @@ -1132,27 +892,10 @@ internal async Task TestGeneratorProducedDiagnostics(bool fullSolutionAnalysis, var service = Assert.IsType(workspace.GetService()); - var gotDiagnostics = false; - service.DiagnosticsUpdated += (s, eCollection) => - { - foreach (var e in eCollection) - { - var diagnostics = e.Diagnostics; - if (diagnostics.Length == 0) - continue; - - var liveId = (LiveDiagnosticUpdateArgsId)e.Id; - if (liveId.Analyzer is GeneratorDiagnosticsPlaceholderAnalyzer) - gotDiagnostics = true; - } - }; - var incrementalAnalyzer = service.CreateIncrementalAnalyzer(workspace); - await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); + var diagnostics = await incrementalAnalyzer.ForceAnalyzeProjectAsync(project, CancellationToken.None); - await ((AsynchronousOperationListener)service.Listener).ExpeditedWaitAsync(); - - Assert.True(gotDiagnostics); + Assert.NotEmpty(diagnostics); } private static Document GetDocumentFromIncompleteProject(AdhocWorkspace workspace) @@ -1170,17 +913,10 @@ private static Document GetDocumentFromIncompleteProject(AdhocWorkspace workspac private static (bool, bool) AnalyzerResultSetter(bool syntax, bool semantic, ImmutableArray diagnostics) { - switch (diagnostics.Length) + foreach (var diagnostic in diagnostics) { - case 0: - break; - case 1: - syntax |= diagnostics[0].Id == Analyzer.s_syntaxRule.Id; - semantic |= diagnostics[0].Id == Analyzer.s_semanticRule.Id; - break; - default: - AssertEx.Fail("shouldn't reach here"); - break; + syntax |= diagnostic.Id == Analyzer.s_syntaxRule.Id; + semantic |= diagnostic.Id == Analyzer.s_semanticRule.Id; } return (syntax, semantic); diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs index d038755b0263b..e0de118a7a3ad 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs @@ -4,14 +4,11 @@ #nullable disable -using System; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticServiceTests.cs deleted file mode 100644 index bd444dac906fd..0000000000000 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticServiceTests.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Test.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics -{ - [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.Diagnostics)] - public class DiagnosticServiceTests - { - private static DiagnosticService GetDiagnosticService(TestWorkspace workspace) - { - var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); - - return diagnosticService; - } - - [Fact] - public void TestCleared() - { - using var workspace = new TestWorkspace(composition: EditorTestCompositions.EditorFeatures); - var mutex = new ManualResetEvent(false); - var document = workspace.CurrentSolution.AddProject("TestProject", "TestProject", LanguageNames.CSharp).AddDocument("TestDocument", string.Empty); - var document2 = document.Project.AddDocument("TestDocument2", string.Empty); - - var diagnosticService = GetDiagnosticService(workspace); - - var source1 = new TestDiagnosticUpdateSource(); - diagnosticService.Register(source1); - - var source2 = new TestDiagnosticUpdateSource(); - diagnosticService.Register(source2); - - diagnosticService.DiagnosticsUpdated += MarkSet; - - // add bunch of data to the service for both sources - RaiseDiagnosticEvent(mutex, source1, workspace, document.Project.Id, document.Id, Tuple.Create(workspace, document)); - RaiseDiagnosticEvent(mutex, source1, workspace, document.Project.Id, document.Id, Tuple.Create(workspace, document.Project, document)); - RaiseDiagnosticEvent(mutex, source1, workspace, document2.Project.Id, document2.Id, Tuple.Create(workspace, document2)); - - RaiseDiagnosticEvent(mutex, source2, workspace, document.Project.Id, null, Tuple.Create(workspace, document.Project)); - RaiseDiagnosticEvent(mutex, source2, workspace, null, null, Tuple.Create(workspace)); - - diagnosticService.DiagnosticsUpdated -= MarkSet; - - // confirm clear for a source - mutex.Reset(); - var count = 0; - diagnosticService.DiagnosticsUpdated += MarkCalled; - - source1.RaiseDiagnosticsClearedEvent(); - - mutex.WaitOne(); - return; - - void MarkCalled(object sender, ImmutableArray args) - { - foreach (var _ in args) - { - // event is serialized. no concurrent call - if (++count == 3) - { - mutex.Set(); - } - } - } - - void MarkSet(object sender, ImmutableArray args) - { - foreach (var _ in args) - mutex.Set(); - } - } - - private static DiagnosticData RaiseDiagnosticEvent(ManualResetEvent set, TestDiagnosticUpdateSource source, TestWorkspace workspace, ProjectId? projectId, DocumentId? documentId, object id) - { - set.Reset(); - - var diagnostic = CreateDiagnosticData(projectId, documentId); - - source.RaiseDiagnosticsUpdatedEvent( - ImmutableArray.Create(DiagnosticsUpdatedArgs.DiagnosticsCreated(id, workspace, workspace.CurrentSolution, projectId, documentId, ImmutableArray.Create(diagnostic)))); - - set.WaitOne(); - - return diagnostic; - } - - private static DiagnosticData CreateDiagnosticData(ProjectId? projectId, DocumentId? documentId) - { - return new DiagnosticData( - id: "test1", - category: "Test", - message: "test1 message", - severity: DiagnosticSeverity.Info, - defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: false, - warningLevel: 1, - customTags: ImmutableArray.Empty, - properties: ImmutableDictionary.Empty, - projectId, - location: new DiagnosticDataLocation(new("originalFile1", new(10, 10), new(20, 20)), documentId)); - } - - private class TestDiagnosticUpdateSource : IDiagnosticUpdateSource - { - public event EventHandler>? DiagnosticsUpdated; - public event EventHandler? DiagnosticsCleared; - - public void RaiseDiagnosticsUpdatedEvent(ImmutableArray args) - => DiagnosticsUpdated?.Invoke(this, args); - - public void RaiseDiagnosticsClearedEvent() - => DiagnosticsCleared?.Invoke(this, EventArgs.Empty); - } - } -} diff --git a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs b/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.cs deleted file mode 100644 index 571b269b2cf55..0000000000000 --- a/src/EditorFeatures/Test/Diagnostics/MockDiagnosticService.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 System; -using System.Collections.Immutable; -using System.Composition; -using System.Linq; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics -{ - [Export(typeof(IDiagnosticService)), Shared, PartNotDiscoverable] - internal class MockDiagnosticService : IDiagnosticService - { - public const string DiagnosticId = "MockId"; - - public event EventHandler> DiagnosticsUpdated { add { } remove { } } - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public MockDiagnosticService() - { - } - } -} diff --git a/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs b/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs index 651608fafd8d8..3d1d7b086fe04 100644 --- a/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs @@ -24,9 +24,7 @@ namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics [UseExportProvider] public class SuppressMessageAttributeWorkspaceTests : SuppressMessageAttributeTests { - private static readonly TestComposition s_compositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)); + private static readonly TestComposition s_compositionWithMockDiagnosticUpdateSourceRegistrationService = EditorTestCompositions.EditorFeatures; private static readonly Lazy _unconditionalSuppressMessageRef = new(() => { diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 9fa1c5ad7ca3a..c72742ee44552 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -2087,8 +2087,13 @@ public async Task HasChanges_Documents(DocumentKind documentKind) [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1371694")] public async Task Project_Add() { + // Project A: var sourceA1 = "class A { void M() { System.Console.WriteLine(1); } }"; + + // Project B (baseline, but not loaded into solution): var sourceB1 = "class B { int F() => 1; }"; + + // Additional documents added to B: var sourceB2 = "class B { int G() => 1; }"; var sourceB3 = "class B { int F() => 2; }"; @@ -2141,7 +2146,7 @@ public async Task Project_Add() }, baseSpans.Select(spans => spans.IsEmpty ? "" : string.Join(",", spans.Select(s => s.LineSpan.ToString())))); var trackedActiveSpans = ImmutableArray.Create( - new ActiveStatementSpan(1, activeLineSpanB1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, unmappedDocumentId: null)); + new ActiveStatementSpan(new ActiveStatementId(0), activeLineSpanB1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame)); var currentSpans = await debuggingSession.GetAdjustedActiveStatementSpansAsync(documentB2, (_, _, _) => new(trackedActiveSpans), CancellationToken.None); // TODO: https://github.com/dotnet/roslyn/issues/1204 @@ -3591,8 +3596,8 @@ public async Task ActiveStatements() EnterBreakState(debuggingSession, activeStatements); - var activeStatementSpan11 = new ActiveStatementSpan(0, activeLineSpan11, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, unmappedDocumentId: null); - var activeStatementSpan12 = new ActiveStatementSpan(1, activeLineSpan12, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, unmappedDocumentId: null); + var activeStatementSpan11 = new ActiveStatementSpan(new ActiveStatementId(0), activeLineSpan11, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame); + var activeStatementSpan12 = new ActiveStatementSpan(new ActiveStatementId(1), activeLineSpan12, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame); var baseSpans = await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(document1.Id), CancellationToken.None); AssertEx.Equal(new[] @@ -3611,8 +3616,8 @@ public async Task ActiveStatements() var document2 = solution.GetDocument(documentId); // tracking span update triggered by the edit: - var activeStatementSpan21 = new ActiveStatementSpan(0, activeLineSpan21, ActiveStatementFlags.NonLeafFrame, unmappedDocumentId: null); - var activeStatementSpan22 = new ActiveStatementSpan(1, activeLineSpan22, ActiveStatementFlags.LeafFrame, unmappedDocumentId: null); + var activeStatementSpan21 = new ActiveStatementSpan(new ActiveStatementId(0), activeLineSpan21, ActiveStatementFlags.NonLeafFrame); + var activeStatementSpan22 = new ActiveStatementSpan(new ActiveStatementId(1), activeLineSpan22, ActiveStatementFlags.LeafFrame); var trackedActiveSpans2 = ImmutableArray.Create(activeStatementSpan21, activeStatementSpan22); currentSpans = await debuggingSession.GetAdjustedActiveStatementSpansAsync(document2, (_, _, _) => new(trackedActiveSpans2), CancellationToken.None); @@ -3671,8 +3676,8 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy var baseSpans = (await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(documentId), CancellationToken.None)).Single(); AssertEx.Equal(new[] { - new ActiveStatementSpan(0, activeLineSpan11, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, unmappedDocumentId: null), - new ActiveStatementSpan(1, activeLineSpan12, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, unmappedDocumentId: null) + new ActiveStatementSpan(new ActiveStatementId(0), activeLineSpan11, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame), + new ActiveStatementSpan(new ActiveStatementId(1), activeLineSpan12, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame) }, baseSpans); // change the source (valid edit): @@ -3801,7 +3806,7 @@ DocumentId AddProjectAndLinkDocument(string projectName, Document doc, SourceTex Assert.Equal(3, baseActiveStatementsMap.InstructionMap.Count); - var statements = baseActiveStatementsMap.InstructionMap.Values.OrderBy(v => v.Ordinal).ToArray(); + var statements = baseActiveStatementsMap.InstructionMap.Values.OrderBy(v => v.Id.Ordinal).ToArray(); var s = statements[0]; Assert.Equal(0x06000001, s.InstructionId.Method.Token); Assert.Equal(module4, s.InstructionId.Method.Module); @@ -4197,7 +4202,7 @@ static void F() var spans = (await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(documentId), CancellationToken.None)).Single(); AssertEx.Equal(new[] { - new ActiveStatementSpan(0, new LinePositionSpan(new(4,41), new(4,42)), ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, unmappedDocumentId: null), + new ActiveStatementSpan(new ActiveStatementId(0), new LinePositionSpan(new(4,41), new(4,42)), ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame), }, spans); solution = solution.WithDocumentText(documentId, CreateText(SourceMarkers.Clear(markedSourceV4))); @@ -4299,8 +4304,8 @@ static void F() var spans = (await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(documentId), CancellationToken.None)).Single(); AssertEx.Equal(new[] { - new ActiveStatementSpan(0, expectedSpanG1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, documentId), - new ActiveStatementSpan(1, expectedSpanF1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, documentId) + new ActiveStatementSpan(new ActiveStatementId(0), expectedSpanG1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, documentId), + new ActiveStatementSpan(new ActiveStatementId(1), expectedSpanF1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, documentId) }, spans); solution = solution.WithDocumentText(documentId, CreateText(SourceMarkers.Clear(markedSource3))); @@ -4312,8 +4317,8 @@ static void F() spans = (await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(documentId), CancellationToken.None)).Single(); AssertEx.Equal(new[] { - new ActiveStatementSpan(0, expectedSpanG2, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, documentId), - new ActiveStatementSpan(1, expectedSpanF2, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, documentId) + new ActiveStatementSpan(new ActiveStatementId(0), expectedSpanG2, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, documentId), + new ActiveStatementSpan(new ActiveStatementId(1), expectedSpanF2, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.NonLeafFrame, documentId) }, spans); // no rude edits: @@ -4408,7 +4413,7 @@ static void F() var spans = (await debuggingSession.GetBaseActiveStatementSpansAsync(solution, ImmutableArray.Create(documentId), CancellationToken.None)).Single(); AssertEx.Equal(new[] { - new ActiveStatementSpan(0, expectedSpanG1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame, unmappedDocumentId: null) + new ActiveStatementSpan(new ActiveStatementId(0), expectedSpanG1, ActiveStatementFlags.MethodUpToDate | ActiveStatementFlags.LeafFrame) // active statement in F has been deleted }, spans); diff --git a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 91df0b1a8e82d..1dc31c4171205 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -10,14 +10,12 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Contracts.EditAndContinue; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.EditAndContinue; -using Microsoft.CodeAnalysis.Contracts.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; @@ -39,8 +37,7 @@ private static string Inspect(DiagnosticData d) (!string.IsNullOrWhiteSpace(d.DataLocation.UnmappedFileSpan.Path) ? $" {d.DataLocation.UnmappedFileSpan.Path}({d.DataLocation.UnmappedFileSpan.StartLinePosition.Line}, {d.DataLocation.UnmappedFileSpan.StartLinePosition.Character}, {d.DataLocation.UnmappedFileSpan.EndLinePosition.Line}, {d.DataLocation.UnmappedFileSpan.EndLinePosition.Character}):" : "") + $" {d.Message}"; - [Theory] - [CombinatorialData] + [Theory, CombinatorialData, Obsolete] public async Task Proxy(TestHost testHost) { var localComposition = EditorTestCompositions.EditorFeatures.WithTestHostParts(testHost) @@ -98,10 +95,10 @@ await localWorkspace.ChangeSolutionAsync(localWorkspace.CurrentSolution var mockDiagnosticService = (MockDiagnosticAnalyzerService)localWorkspace.GetService(); - void VerifyReanalyzeInvocation(ImmutableArray documentIds) + void VerifyReanalyzeInvocation() { - AssertEx.Equal(documentIds, mockDiagnosticService.DocumentsToReanalyze); - mockDiagnosticService.DocumentsToReanalyze.Clear(); + Assert.True(mockDiagnosticService.RequestedRefresh); + mockDiagnosticService.RequestedRefresh = false; } var diagnosticUpdateSource = new EditAndContinueDiagnosticUpdateSource(); @@ -129,7 +126,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) newSpan: new SourceSpan(1, 2, 1, 5)); var activeSpans1 = ImmutableArray.Create( - new ActiveStatementSpan(0, new LinePositionSpan(new LinePosition(1, 2), new LinePosition(3, 4)), ActiveStatementFlags.NonLeafFrame, documentId)); + new ActiveStatementSpan(new ActiveStatementId(0), new LinePositionSpan(new LinePosition(1, 2), new LinePosition(3, 4)), ActiveStatementFlags.NonLeafFrame, documentId)); var activeStatementSpanProvider = new ActiveStatementSpanProvider((documentId, path, cancellationToken) => { @@ -182,7 +179,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) }; await sessionProxy.BreakStateOrCapabilitiesChangedAsync(mockDiagnosticService, diagnosticUpdateSource, inBreakState: true, CancellationToken.None); - VerifyReanalyzeInvocation(ImmutableArray.Create(documentId)); + VerifyReanalyzeInvocation(); Assert.Equal(1, emitDiagnosticsClearedCount); emitDiagnosticsClearedCount = 0; @@ -240,7 +237,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) var (updates, _, _, syntaxErrorData) = await sessionProxy.EmitSolutionUpdateAsync(localWorkspace.CurrentSolution, activeStatementSpanProvider, mockDiagnosticService, diagnosticUpdateSource, CancellationToken.None); AssertEx.Equal($"[{projectId}] Error ENC1001: test.cs(0, 1, 0, 2): {string.Format(FeaturesResources.ErrorReadingFile, "doc", "syntax error")}", Inspect(syntaxErrorData!)); - VerifyReanalyzeInvocation(ImmutableArray.Create(documentId)); + VerifyReanalyzeInvocation(); Assert.Equal(ModuleUpdateStatus.Ready, updates.Status); @@ -281,7 +278,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) }; await sessionProxy.CommitSolutionUpdateAsync(mockDiagnosticService, CancellationToken.None); - VerifyReanalyzeInvocation(ImmutableArray.Create(documentId)); + VerifyReanalyzeInvocation(); // DiscardSolutionUpdate @@ -292,7 +289,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) // GetBaseActiveStatementSpans - var activeStatementSpan1 = new ActiveStatementSpan(0, span1, ActiveStatementFlags.NonLeafFrame | ActiveStatementFlags.PartiallyExecuted, unmappedDocumentId: documentId); + var activeStatementSpan1 = new ActiveStatementSpan(new ActiveStatementId(0), span1, ActiveStatementFlags.NonLeafFrame | ActiveStatementFlags.PartiallyExecuted, UnmappedDocumentId: documentId); mockEncService.GetBaseActiveStatementSpansImpl = (solution, documentIds) => { @@ -339,7 +336,7 @@ void VerifyReanalyzeInvocation(ImmutableArray documentIds) }; await sessionProxy.EndDebuggingSessionAsync(solution, diagnosticUpdateSource, mockDiagnosticService, CancellationToken.None); - VerifyReanalyzeInvocation(ImmutableArray.Create(documentId)); + VerifyReanalyzeInvocation(); Assert.Equal(1, emitDiagnosticsClearedCount); emitDiagnosticsClearedCount = 0; Assert.Empty(emitDiagnosticsUpdated); diff --git a/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs b/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs index 42046b5d37d1c..2190803b49cc7 100644 --- a/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs +++ b/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs @@ -31,7 +31,8 @@ public class StructureTaggerTests public async Task CSharpOutliningTagger( bool collapseRegionsWhenCollapsingToDefinitions, bool showBlockStructureGuidesForDeclarationLevelConstructs, - bool showBlockStructureGuidesForCodeLevelConstructs) + bool showBlockStructureGuidesForCodeLevelConstructs, + bool showBlockStructureGuidesForCommentsAndPreprocessorRegions) { var code = @"using System; @@ -59,6 +60,7 @@ static void Main(string[] args) globalOptions.SetGlobalOption(BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp, collapseRegionsWhenCollapsingToDefinitions); globalOptions.SetGlobalOption(BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForDeclarationLevelConstructs); globalOptions.SetGlobalOption(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForCodeLevelConstructs); + globalOptions.SetGlobalOption(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, LanguageNames.CSharp, showBlockStructureGuidesForCommentsAndPreprocessorRegions); var tags = await GetTagsFromWorkspaceAsync(workspace); @@ -74,7 +76,7 @@ static void Main(string[] args) { Assert.Equal(collapseRegionsWhenCollapsingToDefinitions, regionTag.IsImplementation); Assert.Equal(14, GetCollapsedHintLineCount(regionTag)); - Assert.Equal(PredefinedStructureTagTypes.Nonstructural, regionTag.Type); + Assert.Equal(showBlockStructureGuidesForCommentsAndPreprocessorRegions ? PredefinedStructureTagTypes.PreprocessorRegion : PredefinedStructureTagTypes.Nonstructural, regionTag.Type); Assert.Equal("#region MyRegion", GetHeaderText(regionTag)); }, classTag => diff --git a/src/EditorFeatures/Test/TextEditor/OpenDocumentTests.cs b/src/EditorFeatures/Test/TextEditor/OpenDocumentTests.cs index 39892260717cd..4651264807e91 100644 --- a/src/EditorFeatures/Test/TextEditor/OpenDocumentTests.cs +++ b/src/EditorFeatures/Test/TextEditor/OpenDocumentTests.cs @@ -48,8 +48,9 @@ public void LinkedFiles() // Confirm the files have been linked by file path. This isn't the core part of this test but without it // nothing else will work. - Assert.Equal(documentIds, workspace.CurrentSolution.GetDocumentIdsWithFilePath(FilePath)); - Assert.Equal(new[] { documentIds.Last() }, workspace.CurrentSolution.GetDocument(documentIds.First()).GetLinkedDocumentIds()); + AssertEx.SetEqual(documentIds, workspace.CurrentSolution.GetDocumentIdsWithFilePath(FilePath)); + Assert.Equal(documentIds.Last(), workspace.CurrentSolution.GetDocument(documentIds.First()).GetLinkedDocumentIds().Single()); + Assert.Equal(documentIds.First(), workspace.CurrentSolution.GetDocument(documentIds.Last()).GetLinkedDocumentIds().Single()); // Now the core test: first, if we make a modified version of the source text, and attempt to get the document for it, // both copies should be updated. diff --git a/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb b/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb index 10c96b8aa8e94..e2a84aad56c6e 100644 --- a/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb +++ b/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb @@ -9,6 +9,7 @@ Imports System.Reflection Imports System.Threading Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Copilot Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests @@ -17,6 +18,8 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.ErrorLogger Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.UnitTests Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests @@ -25,9 +28,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Public Class CodeFixServiceTests - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Private ReadOnly _assemblyLoader As IAnalyzerAssemblyLoader = New InMemoryAssemblyLoader() @@ -54,7 +55,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Dim project = workspace.CurrentSolution.Projects(0) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim logger = SpecializedCollections.SingletonEnumerable(New Lazy(Of IErrorLoggerService)(Function() workspace.Services.GetService(Of IErrorLoggerService))) @@ -130,7 +130,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Dim project = workspace.CurrentSolution.Projects(0) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim logger = SpecializedCollections.SingletonEnumerable(New Lazy(Of IErrorLoggerService)(Function() workspace.Services.GetService(Of IErrorLoggerService))) @@ -263,5 +262,86 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests #Enable Warning RS0005 End Function End Class + + + Public Async Function TestCopilotCodeAnalysisServiceWithoutSyntaxTree() As Task + Dim workspaceDefinition = + + + + var x = {}; // e.g., TypeScript code or anything else that doesn't support compilations + + + + + Dim composition = EditorTestCompositions.EditorFeatures.AddParts( + GetType(NoCompilationContentTypeDefinitions), + GetType(NoCompilationContentTypeLanguageService), + GetType(NoCompilationCopilotCodeAnalysisService)) + + Using workspace = EditorTestWorkspace.Create(workspaceDefinition, composition:=composition) + + Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single() + Dim diagnosticsXml = + + + MappedFile=<%= document.Name %> MappedLine="0" MappedColumn="0" + OriginalFile=<%= document.Name %> OriginalLine="0" OriginalColumn="0" + Message=<%= "Test Message" %>/> + + Dim diagnostics = DiagnosticProviderTests.GetExpectedDiagnostics(workspace, diagnosticsXml) + + Dim copilotCodeAnalysisService = document.Project.Services.GetService(Of ICopilotCodeAnalysisService)() + Dim noCompilationCopilotCodeAnalysisService = DirectCast(copilotCodeAnalysisService, NoCompilationCopilotCodeAnalysisService) + + NoCompilationCopilotCodeAnalysisService.Diagnostics = diagnostics.SelectAsArray(Of Diagnostic)( + Function(d) d.ToDiagnosticAsync(document.Project, CancellationToken.None).Result) + Dim codefixService = workspace.ExportProvider.GetExportedValue(Of ICodeFixService) + + ' Make sure we don't crash + Dim unused = Await codefixService.GetMostSevereFixAsync( + document, Text.TextSpan.FromBounds(0, 0), New DefaultCodeActionRequestPriorityProvider(), CodeActionOptions.DefaultProvider, CancellationToken.None) + End Using + End Function + + + Private Class NoCompilationCopilotCodeAnalysisService + Implements ICopilotCodeAnalysisService + + + + Public Sub New() + End Sub + + Public Shared Property Diagnostics As ImmutableArray(Of Diagnostic) = ImmutableArray(Of Diagnostic).Empty + + Public Function IsRefineOptionEnabledAsync() As Task(Of Boolean) Implements ICopilotCodeAnalysisService.IsRefineOptionEnabledAsync + Return Task.FromResult(True) + End Function + + Public Function IsCodeAnalysisOptionEnabledAsync() As Task(Of Boolean) Implements ICopilotCodeAnalysisService.IsCodeAnalysisOptionEnabledAsync + Return Task.FromResult(True) + End Function + + Public Function IsAvailableAsync(cancellationToken As CancellationToken) As Task(Of Boolean) Implements ICopilotCodeAnalysisService.IsAvailableAsync + Return Task.FromResult(True) + End Function + + Public Function GetAvailablePromptTitlesAsync(document As Document, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of String)) Implements ICopilotCodeAnalysisService.GetAvailablePromptTitlesAsync + Return Task.FromResult(ImmutableArray.Create("Title")) + End Function + + Public Function AnalyzeDocumentAsync(document As Document, span As TextSpan?, promptTitle As String, cancellationToken As CancellationToken) As Task Implements ICopilotCodeAnalysisService.AnalyzeDocumentAsync + Return Task.CompletedTask + End Function + + Public Function GetCachedDocumentDiagnosticsAsync(document As Document, span As TextSpan?, promptTitles As ImmutableArray(Of String), cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of Diagnostic)) Implements ICopilotCodeAnalysisService.GetCachedDocumentDiagnosticsAsync + Return Task.FromResult(Diagnostics) + End Function + + Public Function StartRefinementSessionAsync(oldDocument As Document, newDocument As Document, primaryDiagnostic As Diagnostic, cancellationToken As CancellationToken) As Task Implements ICopilotCodeAnalysisService.StartRefinementSessionAsync + Return Task.CompletedTask + End Function + End Class End Class End Namespace diff --git a/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb b/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb index f49e402cad388..1eb3762d50b76 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AbstractCrossLanguageUserDiagnosticTest.vb @@ -28,8 +28,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics Protected Const DestinationDocument = "DestinationDocument" Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService), GetType(WorkspaceTestLogger)) + .AddParts(GetType(WorkspaceTestLogger)) Private Shared ReadOnly s_composition As TestComposition = s_compositionWithMockDiagnosticUpdateSourceRegistrationService _ .AddParts(GetType(TestAddMetadataReferenceCodeActionOperationFactoryWorkspaceService)) diff --git a/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb b/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb index da1d0c1a02234..1f0349b7979f4 100644 --- a/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/AdditionalFileDiagnosticsTests.vb @@ -16,9 +16,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.AdditionalFiles Public Class AdditionalFileDiagnosticsTests Inherits AbstractCrossLanguageUserDiagnosticTest - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace, language As String) As (DiagnosticAnalyzer, CodeFixProvider) Return (New AdditionalFileAnalyzer(), New AdditionalFileFixer()) diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb index 4768b8eab7245..6a22642aa3422 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticProviderTests.vb @@ -37,11 +37,9 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Private Const s_mappedFileAttributeName As String = "MappedFile" Private Shared ReadOnly s_composition As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ .AddParts( GetType(NoCompilationContentTypeLanguageService), - GetType(NoCompilationContentTypeDefinitions), - GetType(MockDiagnosticUpdateSourceRegistrationService)) + GetType(NoCompilationContentTypeDefinitions)) Public Sub TestNoErrors() @@ -296,13 +294,12 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzerReference = New TestAnalyzerReferenceByLanguage(compilerAnalyzersMap) workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences({analyzerReference})) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim analyzerService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Return analyzerService End Function - Private Shared Function GetExpectedDiagnostics(workspace As EditorTestWorkspace, diagnostics As XElement) As List(Of DiagnosticData) + Friend Shared Function GetExpectedDiagnostics(workspace As EditorTestWorkspace, diagnostics As XElement) As List(Of DiagnosticData) Dim result As New List(Of DiagnosticData) Dim mappedLine As Integer, mappedColumn As Integer, originalLine As Integer, originalColumn As Integer Dim Id As String, message As String, originalFile As String, mappedFile As String diff --git a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb index 0427fb528af0a..1f9aec9da2ced 100644 --- a/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb +++ b/src/EditorFeatures/Test2/Diagnostics/DiagnosticServiceTests.vb @@ -28,9 +28,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests <[UseExportProvider]> Public Class DiagnosticServiceTests - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Private ReadOnly _assemblyLoader As IAnalyzerAssemblyLoader = New InMemoryAssemblyLoader() @@ -92,7 +90,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim hostAnalyzers = solution.SolutionState.Analyzers Dim project = solution.Projects(0) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' Verify available diagnostic descriptors/analyzers @@ -198,7 +195,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim project = solution.Projects(0) Dim hostAnalyzers = solution.SolutionState.Analyzers - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' Add project analyzer reference with no analyzers. @@ -243,7 +239,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim projectAnalyzerReference = New AnalyzerImageReference( ImmutableArray.Create(Of DiagnosticAnalyzer)(New TestDiagnosticAnalyzer1(1)), display:=referenceName) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) project = project.WithAnalyzerReferences(ImmutableArray.Create(Of AnalyzerReference)(projectAnalyzerReference)) @@ -280,7 +275,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim project = solution.Projects(0) Dim hostAnalyzers = solution.SolutionState.Analyzers - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' Verify available diagnostic descriptors/analyzers @@ -351,7 +345,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests solution = p2.Solution Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -394,7 +387,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzerReference = New TestAnalyzerReferenceByLanguage(analyzersMap) workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences({analyzerReference})) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService2 = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptors = workspace.CurrentSolution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService2.AnalyzerInfoCache) @@ -443,7 +435,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Dim analyzerReference2 = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location) project = project.AddAnalyzerReference(analyzerReference2) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim analyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) Dim descriptorsMap = workspace.CurrentSolution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -498,7 +489,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -534,7 +524,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -557,35 +546,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests End Using End Function - - Public Sub TestDiagnosticAnalyzerExceptionHandledNoCrash() - Dim test = - - - - - - Using workspace = TestWorkspace.CreateWorkspace(test) - Dim project = workspace.CurrentSolution.Projects.Single() - Dim analyzer = New CodeBlockStartedAnalyzer(Of Microsoft.CodeAnalysis.CSharp.SyntaxKind) - - Dim expected = Diagnostic.Create("test", "test", "test", DiagnosticSeverity.Error, DiagnosticSeverity.Error, True, 0) - Dim exceptionDiagnosticsSource = New TestHostDiagnosticUpdateSource(workspace) - - ' check reporting diagnostic to a project that doesn't exist - exceptionDiagnosticsSource.ReportAnalyzerDiagnostic(analyzer, expected, ProjectId.CreateFromSerialized(Guid.NewGuid(), "dummy")) - Dim diagnostics = exceptionDiagnosticsSource.GetTestAccessor().GetReportedDiagnostics(analyzer) - Assert.Equal(0, diagnostics.Count()) - - ' check workspace diagnostic reporting - exceptionDiagnosticsSource.ReportAnalyzerDiagnostic(analyzer, expected, projectId:=Nothing) - diagnostics = exceptionDiagnosticsSource.GetTestAccessor().GetReportedDiagnostics(analyzer) - - Assert.Equal(1, diagnostics.Count()) - Assert.Equal(expected.Id, diagnostics.First().Id) - End Using - End Sub - Public Async Function TestDiagnosticAnalyzer_FileLoadFailure() As Task Dim test = @@ -611,7 +571,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -635,7 +594,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Using workspace = TestWorkspace.CreateWorkspace(test, composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -681,7 +639,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -716,7 +673,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' Ensure no duplicate diagnostics. @@ -793,7 +749,6 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' Ensure no duplicate diagnostics. @@ -830,7 +785,6 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -989,7 +943,6 @@ class AnonymousFunctions Dim analyzerReference = New AnalyzerImageReference(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer)) project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -1031,7 +984,6 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1088,7 +1040,6 @@ class AnonymousFunctions project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1142,7 +1093,6 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1186,7 +1136,6 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1230,7 +1179,6 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1283,7 +1231,6 @@ End Class project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1347,7 +1294,6 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1395,7 +1341,6 @@ public class B project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -1467,7 +1412,6 @@ public class B project = additionalDoc.Project Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2018,7 +1962,6 @@ End Class project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -2079,7 +2022,6 @@ namespace ConsoleApplication1 project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -2147,7 +2089,6 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2187,7 +2128,6 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2252,7 +2192,6 @@ class C Dim span = localDecl.Span Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2294,7 +2233,6 @@ class C project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) @@ -2345,7 +2283,6 @@ class MyClass Dim span = localDecl.Span Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2398,7 +2335,6 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2469,7 +2405,6 @@ class MyClass project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace) @@ -2564,7 +2499,6 @@ public class C project = project.AddAnalyzerReference(analyzerReference) Dim mefExportProvider = DirectCast(workspace.Services.HostServices, IMefHostExportProvider) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim descriptorsMap = solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(diagnosticService.AnalyzerInfoCache, project) diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb index ed69904f96831..d04b8b108da6d 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb @@ -712,7 +712,7 @@ class c Public Function InvokeWithTrueKeywordCommitSeeLangword(showCompletionInArgumentLists As Boolean) As Task - Return InvokeWithKeywordCommitSeeLangword("true", showCompletionInArgumentLists) + Return InvokeWithKeywordCommitSeeLangword("true", showCompletionInArgumentLists, unique:=False) End Function @@ -740,7 +740,7 @@ class c Return InvokeWithKeywordCommitSeeLangword("await", showCompletionInArgumentLists) End Function - Private Shared Async Function InvokeWithKeywordCommitSeeLangword(keyword As String, showCompletionInArgumentLists As Boolean) As Task + Private Shared Async Function InvokeWithKeywordCommitSeeLangword(keyword As String, showCompletionInArgumentLists As Boolean, Optional unique As Boolean = True) As Task Using state = TestStateFactory.CreateCSharpTestState( $$ diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_XmlDoc.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_XmlDoc.vb index ea5b97d706b47..df068451d6875 100644 --- a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_XmlDoc.vb +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_XmlDoc.vb @@ -637,12 +637,12 @@ End Class Public Function InvokeWithTrueKeywordCommitSeeLangword() As Task - Return InvokeWithKeywordCommitSeeLangword("True") + Return InvokeWithKeywordCommitSeeLangword("True", unique:=False) End Function Public Function InvokeWithFalseKeywordCommitSeeLangword() As Task - Return InvokeWithKeywordCommitSeeLangword("False") + Return InvokeWithKeywordCommitSeeLangword("False", unique:=False) End Function diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs index b962707454438..717b35ad533ba 100644 --- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs +++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassifications.cs @@ -90,6 +90,10 @@ public static FormattedClassification Property(string text) public static FormattedClassification Event(string text) => New(text, ClassificationTypeNames.EventName); + [DebuggerStepThrough] + public static FormattedClassification Obsolete(string text) + => New(text, ClassificationTypeNames.ObsoleteSymbol); + [DebuggerStepThrough] public static FormattedClassification Static(string text) => New(text, ClassificationTypeNames.StaticSymbol); diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index 40c80229ce774..b3fd01ef67bb0 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -24,7 +24,6 @@ internal class DiagnosticTaggerWrapper where TTag : ITag { private readonly EditorTestWorkspace _workspace; - public readonly DiagnosticService DiagnosticService; private readonly IThreadingContext _threadingContext; private readonly IAsynchronousOperationListenerProvider _listenerProvider; @@ -33,7 +32,6 @@ internal class DiagnosticTaggerWrapper public DiagnosticTaggerWrapper( EditorTestWorkspace workspace, IReadOnlyDictionary>? analyzerMap = null, - IDiagnosticUpdateSource? updateSource = null, bool createTaggerProvider = true) { _threadingContext = workspace.GetService(); @@ -49,13 +47,6 @@ public DiagnosticTaggerWrapper( _workspace = workspace; - DiagnosticService = (DiagnosticService)workspace.ExportProvider.GetExportedValue(); - - if (updateSource is object) - { - DiagnosticService.Register(updateSource); - } - if (createTaggerProvider) { _ = TaggerProvider; diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs index cf0f9c741a49f..6bd490efea2e5 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/MockDiagnosticAnalyzerService.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics internal class MockDiagnosticAnalyzerService : IDiagnosticAnalyzerService { private readonly ArrayBuilder<(DiagnosticData Diagnostic, DiagnosticKind KindFilter)> _diagnosticsWithKindFilter; - public readonly List DocumentsToReanalyze = []; + public bool RequestedRefresh; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -41,8 +41,8 @@ public void AddDiagnostics(ImmutableArray diagnostics, Diagnosti AddDiagnostic(diagnostic, diagnosticKind); } - public void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) - => DocumentsToReanalyze.AddRange(documentIds); + public void RequestDiagnosticRefresh() + => RequestedRefresh = true; public DiagnosticAnalyzerInfoCache AnalyzerInfoCache => throw new NotImplementedException(); diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/TestHostDiagnosticUpdateSource.cs b/src/EditorFeatures/TestUtilities/Diagnostics/TestHostDiagnosticUpdateSource.cs deleted file mode 100644 index 12d2dfaa53ca5..0000000000000 --- a/src/EditorFeatures/TestUtilities/Diagnostics/TestHostDiagnosticUpdateSource.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics -{ - internal class TestHostDiagnosticUpdateSource : AbstractHostDiagnosticUpdateSource - { - private readonly Workspace _workspace; - - public TestHostDiagnosticUpdateSource(Workspace workspace) - => _workspace = workspace; - - public override Workspace Workspace - { - get - { - return _workspace; - } - } - - public override int GetHashCode() - => _workspace.GetHashCode(); - } -} diff --git a/src/EditorFeatures/TestUtilities/ObsoleteSymbol/AbstractObsoleteSymbolTests.cs b/src/EditorFeatures/TestUtilities/ObsoleteSymbol/AbstractObsoleteSymbolTests.cs new file mode 100644 index 0000000000000..05057c372ad75 --- /dev/null +++ b/src/EditorFeatures/TestUtilities/ObsoleteSymbol/AbstractObsoleteSymbolTests.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more 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.ObsoleteSymbol; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.ObsoleteSymbol; + +[UseExportProvider] +public abstract class AbstractObsoleteSymbolTests +{ + protected abstract EditorTestWorkspace CreateWorkspace(string markup); + + protected async Task TestAsync(string markup) + { + using var workspace = CreateWorkspace(markup); + + var project = workspace.CurrentSolution.Projects.Single(); + var language = project.Language; + var documents = project.Documents.ToImmutableArray(); + + for (var i = 0; i < documents.Length; i++) + { + var document = documents[i]; + var text = await document.GetTextAsync(); + + var service = document.GetRequiredLanguageService(); + var textSpans = ImmutableArray.Create(new TextSpan(0, text.Length)); + var result = await service.GetLocationsAsync(document, textSpans, CancellationToken.None); + + var expectedSpans = workspace.Documents[i].SelectedSpans.OrderBy(s => s.Start); + var actualSpans = result.OrderBy(s => s.Start); + + AssertEx.EqualOrDiff( + string.Join(Environment.NewLine, expectedSpans), + string.Join(Environment.NewLine, actualSpans)); + } + } +} diff --git a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs index b18dd1053b470..79108121d640b 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs @@ -5,14 +5,10 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text.Tagging; using Roslyn.Utilities; @@ -29,29 +25,6 @@ internal sealed class TestDiagnosticTagProducer return SquiggleUtilities.GetDiagnosticsAndErrorSpansAsync(workspace, analyzerMap); } - internal static async Task>> GetErrorsFromUpdateSource(EditorTestWorkspace workspace, DiagnosticsUpdatedArgs updateArgs, DiagnosticKind diagnosticKind) - { - var source = new TestDiagnosticUpdateSource(); - - var wrapper = new DiagnosticTaggerWrapper(workspace, updateSource: source); - - var firstDocument = workspace.Documents.First(); - var tagger = wrapper.TaggerProvider.CreateTagger(firstDocument.GetTextBuffer()); - using var disposable = (IDisposable)tagger; - - var analyzerServer = (MockDiagnosticAnalyzerService)workspace.GetService(); - analyzerServer.AddDiagnostics(updateArgs.Diagnostics, diagnosticKind); - - source.RaiseDiagnosticsUpdated(ImmutableArray.Create(updateArgs)); - - await wrapper.WaitForTags(); - - var snapshot = firstDocument.GetTextBuffer().CurrentSnapshot; - var spans = tagger.GetTags(snapshot.GetSnapshotSpanCollection()).ToImmutableArray(); - - return spans; - } - internal static DiagnosticData CreateDiagnosticData(EditorTestHostDocument document, TextSpan span) { Contract.ThrowIfNull(document.FilePath); @@ -72,15 +45,4 @@ internal static DiagnosticData CreateDiagnosticData(EditorTestHostDocument docum location: new DiagnosticDataLocation(new FileLinePositionSpan(document.FilePath, linePosSpan), document.Id), language: document.Project.Language); } - - private class TestDiagnosticUpdateSource : IDiagnosticUpdateSource - { - public void RaiseDiagnosticsUpdated(ImmutableArray args) - { - DiagnosticsUpdated?.Invoke(this, args); - } - - public event EventHandler>? DiagnosticsUpdated; - public event EventHandler DiagnosticsCleared { add { } remove { } } - } } diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests.vb index e811c3dceb130..d6efc8e53c121 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/GenerateType/GenerateTypeTests.vb @@ -21,9 +21,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Genera ' TODO Requires Wpf due to IInlineRenameService dependency (https: //github.com/dotnet/roslyn/issues/46153) Protected Overrides Function GetComposition() As TestComposition - Return EditorTestCompositions.EditorFeaturesWpf _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Return EditorTestCompositions.EditorFeaturesWpf End Function Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/Preview/PreviewTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/Preview/PreviewTests.vb index e0453e411f8f7..056450b1ef0d3 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/Preview/PreviewTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/Preview/PreviewTests.vb @@ -18,9 +18,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings Inherits AbstractVisualBasicCodeActionTest Private Shared ReadOnly s_composition As TestComposition = EditorTestCompositions.EditorFeaturesWpf _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ .AddParts( - GetType(MockDiagnosticUpdateSourceRegistrationService), GetType(MockPreviewPaneService)) Private Const s_addedDocumentName As String = "AddedDocument" diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb index 7adcdeb7c3f4a..a4dc59fd809d3 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/XmlDocCommentCompletionProviderTests.vb @@ -847,7 +847,9 @@ Class C End Sub End Class " - Await VerifyItemsExistAsync(text, "Nothing", "True", "False", "Await") + For Each keywordKind In SyntaxFacts.GetKeywordKinds() + Await VerifyItemExistsAsync(text, SyntaxFacts.GetText(keywordKind), glyph:=Glyph.Keyword) + Next End Function diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb index fa8c764e075fa..4a19bc92bab85 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.vb @@ -14,9 +14,7 @@ Imports Microsoft.CodeAnalysis.UnitTests.Diagnostics <[UseExportProvider]> Public Class DiagnosticAnalyzerDriverTests - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Public Async Function DiagnosticAnalyzerDriverAllInOne() As Task diff --git a/src/EditorFeatures/VisualBasicTest/ObsoleteSymbol/VisualBasicObsoleteSymbolTests.vb b/src/EditorFeatures/VisualBasicTest/ObsoleteSymbol/VisualBasicObsoleteSymbolTests.vb new file mode 100644 index 0000000000000..fd6fee9bb0372 --- /dev/null +++ b/src/EditorFeatures/VisualBasicTest/ObsoleteSymbol/VisualBasicObsoleteSymbolTests.vb @@ -0,0 +1,218 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.Editor.UnitTests.ObsoleteSymbol + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ObsoleteSymbol + Public Class VisualBasicObsoleteSymbolTests + Inherits AbstractObsoleteSymbolTests + + Protected Overrides Function CreateWorkspace(markup As String) As EditorTestWorkspace + Return EditorTestWorkspace.CreateVisualBasic(markup) + End Function + + + + + + + + Public Async Function TestObsoleteTypeDefinition(keyword As String) As Task + Await TestAsync( + $" + + {keyword} [|ObsoleteType|] + End {keyword} + + {keyword} NonObsoleteType + End {keyword} + ") + End Function + + + Public Async Function TestObsoleteDelegateTypeDefinition() As Task + Await TestAsync( + " + + Delegate Sub [|ObsoleteType|]() + + Delegate Sub NonObsoleteType() + ") + End Function + + + Public Async Function TestDeclarationAndUseOfObsoleteAlias() As Task + Await TestAsync( + " + Imports [|ObsoleteAlias|] = [|ObsoleteType|] + + + Class [|ObsoleteType|] + End Class + + ''' + ''' + Class NonObsoleteType + Dim field As [|ObsoleteAlias|] = New [|ObsoleteType|]() + End Class + ") + End Function + + + Public Async Function TestParametersAndReturnTypes() As Task + Await TestAsync( + " + + Class [|ObsoleteType|] + End Class + + Class NonObsoleteType + Function Method(arg As [|ObsoleteType|]) As [|ObsoleteType|] + Return New [|ObsoleteType|]() + End Function + + Dim field As System.Func(Of [|ObsoleteType|], [|ObsoleteType|]) = Function(arg As [|ObsoleteType|]) New [|ObsoleteType|]() + End Class + ") + End Function + + + Public Async Function TestImplicitType() As Task + Await TestAsync( + " + + Class [|ObsoleteType|] + Public Sub New() + End Sub + + + Public Sub New(x As Integer) + End Sub + End Class + + Class ObsoleteCtor + Public Sub New() + End Sub + + + Public Sub New(x As Integer) + End Sub + End Class + + Class NonObsoleteType + Sub Method() + Dim t1 As New [|ObsoleteType|]() + Dim t2 As [|New|] [|ObsoleteType|](3) + [|Dim|] t3 = New [|ObsoleteType|]() + [|Dim|] t4 = [|New|] [|ObsoleteType|](3) + Dim t5 As [|ObsoleteType|] = New [|ObsoleteType|]() + Dim t6 As [|ObsoleteType|] = [|New|] [|ObsoleteType|](3) + [|Dim|] t7 = CreateObsoleteType() + Dim t8 = NameOf([|ObsoleteType|]) + + Dim u1 As New ObsoleteCtor() + Dim u2 As [|New|] ObsoleteCtor(3) + Dim u3 = New ObsoleteCtor() + Dim u4 = [|New|] ObsoleteCtor(3) + Dim u5 As ObsoleteCtor = New ObsoleteCtor() + Dim u6 As ObsoleteCtor = [|New|] ObsoleteCtor(3) + Dim u8 = NameOf(ObsoleteCtor) + End Sub + + Function CreateObsoleteType() As [|ObsoleteType|] + Return New [|ObsoleteType|]() + End Function + End Class + ") + End Function + + + Public Async Function TestDeclarators() As Task + Await TestAsync( + " + + Class [|ObsoleteType|] + End Class + + Class NonObsoleteType + Sub Method() + ' In this method, only t5 has an implicit type, but the Dim keyword applies to all declared + ' variables. Currently this feature does not analyze a Dim keyword when more than one variable + ' is declared. + Dim t1, t2 As New [|ObsoleteType|](), t3, t4 As [|ObsoleteType|], t5 = New [|ObsoleteType|]() + End Sub + End Class + ") + End Function + + + Public Async Function TestExtensionMethods() As Task + Await TestAsync( + " + + Module [|ObsoleteType|] + + Public Shared Sub ObsoleteMember1(ignored As C) + End Sub + + + + Public Shared Sub [|ObsoleteMember2|](ignored As C) + End Sub + End Module + + Class C + Sub Method() + Me.ObsoleteMember1() + Me.[|ObsoleteMember2|]() + [|ObsoleteType|].ObsoleteMember1(Me) + [|ObsoleteType|].[|ObsoleteMember2|](Me) + End Sub + End Class + ") + End Function + + + Public Async Function TestGenerics() As Task + Await TestAsync( + " + + Class [|ObsoleteType|] + End Class + + + Structure [|ObsoleteValueType|] + End Structure + + Class G(Of T) + End Class + + Class C + Sub M(Of T)() + End Sub + + ''' + ''' Visual Basic, unlike C#, resolves concrete type names in generic argument positions in doc + ''' comment references. + ''' + ''' + Sub Method() + Dim x1 = New G(Of [|ObsoleteType|])() + Dim x2 = New G(Of G(Of [|ObsoleteType|]))() + M(Of [|ObsoleteType|])() + M(Of G(Of [|ObsoleteType|]))() + M(Of G(Of G(Of [|ObsoleteType|])))() + + ' Mark 'Dim' as obsolete even when it points to Nullable(Of T) where T is obsolete + [|Dim|] nullableValue = CreateNullableValueType() + End Sub + + Function CreateNullableValueType() As [|ObsoleteValueType|]? + Return New [|ObsoleteValueType|]() + End Function + End Class + ") + End Function + End Class +End Namespace diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.vb index ae6db766bd0ca..7c0b9a9af9400 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.vb @@ -68,7 +68,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator ' CreateVariable(type As Type, name As String) Dim method = PlaceholderLocalSymbol.GetIntrinsicMethod(compilation, ExpressionCompilerConstants.CreateVariableMethodName) - Dim type = New BoundGetType(syntax, New BoundTypeExpression(syntax, local.Type), typeType) + Dim type = New BoundGetType(syntax, New BoundTypeExpression(syntax, local.Type), + DirectCast(compilation.GetWellKnownTypeMember(WellKnownMember.System_Type__GetTypeFromHandle), MethodSymbol), + typeType) Dim name = New BoundLiteral(syntax, ConstantValue.Create(local.Name), stringType) Dim customTypeInfoPayloadId = New BoundObjectCreationExpression(syntax, Nothing, ImmutableArray(Of BoundExpression).Empty, Nothing, guidType) Dim customTypeInfoPayload = New BoundLiteral(syntax, ConstantValue.Null, byteArrayType) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs index b950e664dfc13..f19c7ca86ed08 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -17,7 +16,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -37,6 +36,27 @@ public XmlDocCommentCompletionProvider() : base(s_defaultRules) { } + private static readonly ImmutableArray s_keywordNames; + + static XmlDocCommentCompletionProvider() + { + using var _ = ArrayBuilder.GetInstance(out var keywordsBuilder); + + foreach (var keywordKind in SyntaxFacts.GetKeywordKinds()) + { + var keywordText = SyntaxFacts.GetText(keywordKind); + + // There are several very special keywords like `__makeref`, which are not intended for pubic use. + // They all start with `_`, so we are filtering them here + if (keywordText[0] != '_') + { + keywordsBuilder.Add(keywordText); + } + } + + s_keywordNames = keywordsBuilder.ToImmutable(); + } + internal override string Language => LanguageNames.CSharp; public override bool IsInsertionTrigger(SourceText text, int characterPosition, CompletionOptions options) @@ -313,18 +333,8 @@ private static bool IsAttributeValueContext(SyntaxToken token, [NotNullWhen(true return false; } - protected override IEnumerable GetKeywordNames() - { - yield return SyntaxFacts.GetText(SyntaxKind.NullKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.StaticKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.VirtualKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.TrueKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.FalseKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.AbstractKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.SealedKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.AsyncKeyword); - yield return SyntaxFacts.GetText(SyntaxKind.AwaitKeyword); - } + protected override ImmutableArray GetKeywordNames() + => s_keywordNames; protected override IEnumerable GetExistingTopLevelElementNames(DocumentationCommentTriviaSyntax syntax) => syntax.Content.Select(GetElementName).WhereNotNull(); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs index 288947677d05c..54e09fcf7e941 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs @@ -68,11 +68,17 @@ private static bool IsTypeParameterConstraintContext(CSharpSyntaxContext context // void Goo() | if (token.Kind() == SyntaxKind.CloseParenToken && - token.Parent.IsKind(SyntaxKind.ParameterList) && - token.Parent.IsParentKind(SyntaxKind.MethodDeclaration)) + token.Parent.IsKind(SyntaxKind.ParameterList)) { - var decl = token.GetAncestor(); - if (decl != null && decl.Arity > 0) + var tokenParent = token.Parent; + if (tokenParent.IsParentKind(SyntaxKind.MethodDeclaration, out var methodDeclaration)) + { + if (methodDeclaration.Arity > 0) + { + return true; + } + } + else if (tokenParent.Parent is LocalFunctionStatementSyntax { TypeParameterList.Parameters.Count: > 0 }) { return true; } diff --git a/src/Features/CSharp/Portable/Copilot/CSharpCopilotCodeFixProvider.cs b/src/Features/CSharp/Portable/Copilot/CSharpCopilotCodeFixProvider.cs index 1718fea285747..0dd257b5b9d27 100644 --- a/src/Features/CSharp/Portable/Copilot/CSharpCopilotCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/Copilot/CSharpCopilotCodeFixProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -71,20 +70,17 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) var hasMultiplePrompts = promptTitles.Length > 1; - // Find the containing method, if any, and also update the fix span to the entire method. - // TODO: count location in doc-comment as part of the method. + // Find the containing method for each diagnostic, and register a fix if any part of the method interect with context span. var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var containingMethod = CSharpSyntaxFacts.Instance.GetContainingMethodDeclaration(root, context.Span.Start, useFullSpan: false); - if (containingMethod is not BaseMethodDeclarationSyntax) - return; - foreach (var diagnostic in context.Diagnostics) { - Debug.Assert(containingMethod.FullSpan.IntersectsWith(diagnostic.Location.SourceSpan)); - - var fix = TryGetFix(document, containingMethod, diagnostic, hasMultiplePrompts); - if (fix != null) - context.RegisterCodeFix(fix, diagnostic); + var containingMethod = CSharpSyntaxFacts.Instance.GetContainingMethodDeclaration(root, diagnostic.Location.SourceSpan.Start, useFullSpan: false); + if (containingMethod?.Span.IntersectsWith(context.Span) is true) + { + var fix = TryGetFix(document, containingMethod, diagnostic, hasMultiplePrompts); + if (fix != null) + context.RegisterCodeFix(fix, diagnostic); + } } } @@ -105,8 +101,8 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) // Parse the proposed Copilot fix into a method declaration. // Guard against failure cases where the proposed fixed code does not parse into a method declaration. // TODO: consider do this early when we create the diagnostic and add a flag in the property bag to speedup lightbulb computation - var fixMethodDeclaration = SyntaxFactory.ParseMemberDeclaration(fix, options: method.SyntaxTree.Options); - if (fixMethodDeclaration is null || !fixMethodDeclaration.IsKind(SyntaxKind.MethodDeclaration) || fixMethodDeclaration.GetDiagnostics().Count() > 3) + var memberDeclaration = SyntaxFactory.ParseMemberDeclaration(fix, options: method.SyntaxTree.Options); + if (memberDeclaration is null || memberDeclaration is not BaseMethodDeclarationSyntax baseMethodDeclaration || baseMethodDeclaration.GetDiagnostics().Count() > 3) return null; var title = hasMultiplePrompts @@ -125,9 +121,9 @@ async Task GetFixedDocumentAsync(SyntaxNode method, string fix, Cancel var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); // TODO: Replace all the whitespace trivia with elastic trivia, and any other trivia related improvements - var newMethod = fixMethodDeclaration - .WithLeadingTrivia(fixMethodDeclaration.HasLeadingTrivia ? fixMethodDeclaration.GetLeadingTrivia() : method.GetLeadingTrivia()) - .WithTrailingTrivia(fixMethodDeclaration.HasTrailingTrivia ? fixMethodDeclaration.GetTrailingTrivia() : method.GetTrailingTrivia()) + var newMethod = memberDeclaration + .WithLeadingTrivia(memberDeclaration.HasLeadingTrivia ? memberDeclaration.GetLeadingTrivia() : method.GetLeadingTrivia()) + .WithTrailingTrivia(memberDeclaration.HasTrailingTrivia ? memberDeclaration.GetTrailingTrivia() : method.GetTrailingTrivia()) .WithAdditionalAnnotations(Formatter.Annotation, WarningAnnotation); editor.ReplaceNode(method, newMethod); diff --git a/src/Features/CSharp/Portable/SemanticSearch/CSharpSemanticSearchService.cs b/src/Features/CSharp/Portable/SemanticSearch/CSharpSemanticSearchService.cs new file mode 100644 index 0000000000000..f4573b8e00525 --- /dev/null +++ b/src/Features/CSharp/Portable/SemanticSearch/CSharpSemanticSearchService.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET6_0_OR_GREATER + +using System; +using System.Collections.Generic; +using System.Composition; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.SemanticSearch.CSharp; + +[ExportLanguageService(typeof(ISemanticSearchService), LanguageNames.CSharp), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpSemanticSearchService() : AbstractSemanticSearchService() +{ + protected override Compilation CreateCompilation( + SourceText query, + IEnumerable references, + SolutionServices services, + out SyntaxTree queryTree, + CancellationToken cancellationToken) + { + var syntaxTreeFactory = services.GetRequiredLanguageService(LanguageNames.CSharp); + + var globalUsingsTree = syntaxTreeFactory.ParseSyntaxTree( + filePath: null, + CSharpSemanticSearchUtilities.ParseOptions, + SemanticSearchUtilities.CreateSourceText(CSharpSemanticSearchUtilities.Configuration.GlobalUsings), + cancellationToken); + + queryTree = syntaxTreeFactory.ParseSyntaxTree( + filePath: SemanticSearchUtilities.QueryDocumentName, + CSharpSemanticSearchUtilities.ParseOptions, + query, + cancellationToken); + + return CSharpCompilation.Create( + assemblyName: SemanticSearchUtilities.QueryProjectName, + [queryTree, globalUsingsTree], + references, + CSharpSemanticSearchUtilities.CompilationOptions); + } +} + +#endif diff --git a/src/Features/CSharp/Portable/SemanticSearch/CSharpSemanticSearchUtilities.cs b/src/Features/CSharp/Portable/SemanticSearch/CSharpSemanticSearchUtilities.cs new file mode 100644 index 0000000000000..77c6da34af215 --- /dev/null +++ b/src/Features/CSharp/Portable/SemanticSearch/CSharpSemanticSearchUtilities.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more 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; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal sealed class CSharpSemanticSearchUtilities +{ + public static readonly CSharpParseOptions ParseOptions = CSharpParseOptions.Default; + public static readonly CSharpCompilationOptions CompilationOptions = new(OutputKind.ConsoleApplication); + + public static readonly SemanticSearchProjectConfiguration Configuration = new() + { + Language = LanguageNames.CSharp, + Query = """ + static IEnumerable Find(Compilation compilation) + { + return compilation.GlobalNamespace.GetMembers("C"); + } + """, + GlobalUsings = """ + global using System; + global using System.Collections.Generic; + global using System.Collections.Immutable; + global using System.Linq; + global using System.Threading; + global using System.Threading.Tasks; + global using Microsoft.CodeAnalysis; + """, + EditorConfig = """ + is_global = true + + dotnet_analyzer_diagnostic.category-Documentation.severity = none + dotnet_analyzer_diagnostic.category-Globalization.severity = none + dotnet_analyzer_diagnostic.category-Interoperability.severity = none + dotnet_analyzer_diagnostic.category-Design.severity = none + dotnet_analyzer_diagnostic.category-Naming.severity = none + dotnet_analyzer_diagnostic.category-Maintainability.severity = none + dotnet_analyzer_diagnostic.category-Style.severity = none + + # CS8321: unused local function + dotnet_diagnostic.CS8321.severity = none + + # IDE051: private member is unused + dotnet_diagnostic.IDE051.severity = none + """, + ParseOptions = ParseOptions, + CompilationOptions = CompilationOptions + }; +} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index d6a1f3ed5ce50..b17a7ac3590d4 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Použít {0} Apply Copilot suggestion - Apply Copilot suggestion + Použít návrh Copilotu @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Použít opravu z @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Upozornění: Návrhy umělé inteligence můžou být nepřesné. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 14491398a0afd..02660912cf6ee 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Anwenden von "{0}" Apply Copilot suggestion - Apply Copilot suggestion + Copilot-Vorschlag anwenden @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Korrektur anwenden von @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Warnung: KI-Vorschläge sind möglicherweise ungenau. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 046e19d1f2372..f2814210ca73a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Aplicar "{0}" Apply Copilot suggestion - Apply Copilot suggestion + Aplicar sugerencia de Copilot @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Aplicar corrección desde @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Advertencia: es posible que las sugerencias de inteligencia artificial no sean precisas. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 03d94573bace4..29e448463f800 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Appliquer « {0} » Apply Copilot suggestion - Apply Copilot suggestion + Appliquer une suggestion Copilot @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Appliquer le correctif à partir de @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Avertissement : il est possible que les suggestions IA soient inexactes. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index e33829f8faa01..b41883686cb23 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Applicare '{0}' Apply Copilot suggestion - Apply Copilot suggestion + Applicare suggerimento di Copilot @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Applicare correzione da @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Avviso: i suggerimenti dell'intelligenza artificiale potrebbero non essere accurati. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index aff6049697ee7..bd88b4053d763 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + '{0}' を適用する Apply Copilot suggestion - Apply Copilot suggestion + Copilot の提案を適用する @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + 修正プログラムの適用元 @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + 警告: AI の提案は不正確である可能性があります。 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 34dd67a3519c7..bd4db9829f81a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + ‘{0}’ 적용 Apply Copilot suggestion - Apply Copilot suggestion + Copilot 제안 적용 @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + 다음에서 수정 사항 적용 @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + 경고: AI 제안은 정확하지 않을 수 있습니다. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 8dc9e15b0f39e..386a40445065f 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Zastosuj „{0}” Apply Copilot suggestion - Apply Copilot suggestion + Zastosuj sugestię funkcji Copilot @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Zastosuj poprawkę z @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Uwaga: sugestie sztucznej inteligencji mogą być niedokładne. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index b9e63880cd1ba..f04c9b23cad52 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Aplicar "{0} Apply Copilot suggestion - Apply Copilot suggestion + Aplicar a sugestão do Copilot @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Aplicar correção de @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Aviso: As sugestões de IA podem ser imprecisas. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 5681aac9e1314..6b1f679835cbd 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + Применить "{0}" Apply Copilot suggestion - Apply Copilot suggestion + Применить предложение Copilot @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Применить исправление из @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Предупреждение: предложения ИИ могут быть неточными. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 98f5ac0813cea..88c82b816a35b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + '{0}' uygula Apply Copilot suggestion - Apply Copilot suggestion + Copilot önerisini uygula @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + Düzeltmeyi uygulama kaynağı @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + Uyarı: Yapay zeka önerileri yanlış olabilir. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 596c0cb3532ca..f44e0e84dbeff 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + 应用“{0}” Apply Copilot suggestion - Apply Copilot suggestion + 应用 Copilot 的建议 @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + 应用来自以下位置的修复 @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + 警告:AI 建议可能不准确。 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index f0ac9a3cfb1ac..8296579c41354 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -24,12 +24,12 @@ Apply '{0}' - Apply '{0}' + 套用 '{0}' Apply Copilot suggestion - Apply Copilot suggestion + 套用 Copilot 建議 @@ -69,7 +69,7 @@ Apply fix from - Apply fix from + 套用修正來源 @@ -254,7 +254,7 @@ Warning: AI suggestions might be inaccurate. - Warning: AI suggestions might be inaccurate. + 警告: AI 建議可能不正確。 diff --git a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs index c386b7144eb9e..f983bd0388233 100644 --- a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs +++ b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionAllCodeTests.cs @@ -24,9 +24,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.Suppression public class CSharpSuppressionAllCodeTests : AbstractSuppressionAllCodeTests { private static readonly TestComposition s_compositionWithMockDiagnosticUpdateSourceRegistrationService = FeaturesTestCompositions.Features - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)) - .AddAssemblies(typeof(DiagnosticService).Assembly); + .AddAssemblies(typeof(DiagnosticAnalyzerService).Assembly); protected override TestWorkspace CreateWorkspaceFromFile(string definition, ParseOptions parseOptions) => TestWorkspace.CreateCSharp(definition, (CSharpParseOptions)parseOptions, composition: s_compositionWithMockDiagnosticUpdateSourceRegistrationService); diff --git a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs index 708742131ab72..e49fb03985192 100644 --- a/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs +++ b/src/Features/CSharpTest/Diagnostics/Suppression/SuppressionTests.cs @@ -448,7 +448,6 @@ void Method() var analyzerReference = new AnalyzerImageReference(ImmutableArray.Create(new CSharpCompilerDiagnosticAnalyzer())); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); - Assert.IsType(workspace.ExportProvider.GetExportedValue()); var diagnosticService = Assert.IsType(workspace.ExportProvider.GetExportedValue()); var incrementalAnalyzer = diagnosticService.CreateIncrementalAnalyzer(workspace); var suppressionProvider = CreateDiagnosticProviderAndFixer(workspace).Item2; diff --git a/src/Features/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/Features/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index c6d22fe6e7ece..8c76e4a890056 100644 --- a/src/Features/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -119,12 +119,13 @@ private static async Task AnalyzeDocumentAsync( Project oldProject, Document newDocument, ActiveStatementsMap activeStatementMap = null, - EditAndContinueCapabilities capabilities = EditAndContinueTestHelpers.Net5RuntimeCapabilities) + EditAndContinueCapabilities capabilities = EditAndContinueTestHelpers.Net5RuntimeCapabilities, + ImmutableArray newActiveStatementSpans = default) { var analyzer = new CSharpEditAndContinueAnalyzer(); var baseActiveStatements = AsyncLazy.Create(activeStatementMap ?? ActiveStatementsMap.Empty); var lazyCapabilities = AsyncLazy.Create(capabilities); - return await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, [], lazyCapabilities, CancellationToken.None); + return await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, newActiveStatementSpans.NullToEmpty(), lazyCapabilities, CancellationToken.None); } #endregion @@ -319,7 +320,7 @@ public static void Main() { KeyValuePairUtil.Create(newDocument.FilePath, ImmutableArray.Create( new ActiveStatement( - ordinal: 0, + new ActiveStatementId(0), ActiveStatementFlags.LeafFrame, new SourceFileSpan(newDocument.FilePath, oldStatementSpan), instructionId: default))) diff --git a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs index 2d165b775dc31..55ab94f4c6afa 100644 --- a/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs +++ b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs @@ -520,7 +520,7 @@ class Example }, ExpectedDiagnostics = { - // /0/Test3.cs(7,10): error CS8618: Non-nullable field 'value' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // /0/Test3.cs(7,10): error CS8618: Non-nullable field 'value' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable. DiagnosticResult.CompilerError("CS8618").WithSpan("/0/Test3.cs", 7, 10, 7, 15).WithSpan("/0/Test3.cs", 7, 10, 7, 15).WithArguments("field", "value"), }, }, diff --git a/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj b/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj index ac384219120d4..801b6a5a0c0e4 100644 --- a/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj +++ b/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj @@ -13,8 +13,30 @@ + + + + + SemanticSearch_RefAssemblies + TargetFramework=$(NetRoslyn) + + + + + + + + + + + + + diff --git a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs new file mode 100644 index 0000000000000..02d2a79a8fc74 --- /dev/null +++ b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.SemanticSearch; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SemanticSearch; + +[UseExportProvider] +public sealed class CSharpSemanticSearchServiceTests +{ + private static readonly string s_referenceAssembliesDir = Path.Combine(Path.GetDirectoryName(typeof(CSharpSemanticSearchServiceTests).Assembly.Location!)!, "SemanticSearchRefs"); + private static readonly char[] s_lineBreaks = ['\r', '\n']; + + private static string Inspect(DefinitionItem def) + => string.Join("", def.DisplayParts.Select(p => p.Text)); + + private static string InspectLine(int position, string text) + { + var lineStart = text.LastIndexOfAny(s_lineBreaks, position, position) + 1; + var lineEnd = text.IndexOfAny(s_lineBreaks, position); + if (lineEnd < 0) + { + lineEnd = text.Length; + } + + return text[lineStart..lineEnd].Trim(); + } + + private static string Inspect(UserCodeExceptionInfo info, string query) + => $"{info.ProjectDisplayName}: {info.Span} '{InspectLine(info.Span.Start, query)}': {info.TypeName.JoinText()}: '{info.Message}'"; + + [ConditionalFact(typeof(CoreClrOnly))] + public async Task SimpleQuery() + { + using var workspace = TestWorkspace.Create(""" + + + + namespace N + { + public partial class C + { + public void VisibleMethod() { } + } + } + + + + """, composition: FeaturesTestCompositions.Features); + + var solution = workspace.CurrentSolution; + + var service = solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); + + var query = """ + static IEnumerable Find(Compilation compilation) + { + return compilation.GlobalNamespace.GetMembers("N"); + } + """; + + var results = new List(); + var observer = new MockSemanticSearchResultsObserver() { OnDefinitionFoundImpl = results.Add }; + var traceSource = new TraceSource("test"); + + var options = workspace.GlobalOptions.GetClassificationOptionsProvider(); + var result = await service.ExecuteQueryAsync(solution, query, s_referenceAssembliesDir, observer, options, traceSource, CancellationToken.None); + + Assert.Null(result.ErrorMessage); + AssertEx.Equal(new[] { "namespace N" }, results.Select(Inspect)); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public async Task ForcedCancellation() + { + using var workspace = TestWorkspace.Create(""" + + + + public class C + { + } + + + + """, composition: FeaturesTestCompositions.Features); + + var solution = workspace.CurrentSolution; + + var service = solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); + + var query = """ + static IEnumerable Find(Compilation compilation) + { + yield return compilation.GlobalNamespace.GetMembers("C").First(); + + while (true) + { + + } + } + """; + + var cancellationSource = new CancellationTokenSource(); + var exceptions = new List(); + + var observer = new MockSemanticSearchResultsObserver() + { + // cancel on first result: + OnDefinitionFoundImpl = _ => cancellationSource.Cancel(), + OnUserCodeExceptionImpl = exceptions.Add + }; + + var traceSource = new TraceSource("test"); + var options = workspace.GlobalOptions.GetClassificationOptionsProvider(); + + await Assert.ThrowsAsync( + () => service.ExecuteQueryAsync(solution, query, s_referenceAssembliesDir, observer, options, traceSource, cancellationSource.Token)); + + Assert.Empty(exceptions); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public async Task StackOverflow() + { + using var workspace = TestWorkspace.Create(""" + + + + public class C + { + } + + + + """, composition: FeaturesTestCompositions.Features); + + var solution = workspace.CurrentSolution; + + var service = solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); + + var query = """ + static IEnumerable Find(Compilation compilation) + { + yield return compilation.GlobalNamespace.GetMembers("C").First(); + F(0); + void F(long x) + { + F(x + 1); + } + } + """; + + var exceptions = new List(); + var observer = new MockSemanticSearchResultsObserver() + { + OnUserCodeExceptionImpl = exceptions.Add + }; + + var traceSource = new TraceSource("test"); + var options = workspace.GlobalOptions.GetClassificationOptionsProvider(); + + var result = await service.ExecuteQueryAsync(solution, query, s_referenceAssembliesDir, observer, options, traceSource, CancellationToken.None); + var expectedMessage = new InsufficientExecutionStackException().Message; + AssertEx.Equal(string.Format(FeaturesResources.Semantic_search_query_terminated_with_exception, "CSharpAssembly1", expectedMessage), result.ErrorMessage); + + var exception = exceptions.Single(); + AssertEx.Equal($"CSharpAssembly1: [179..179) 'F(x + 1);': InsufficientExecutionStackException: '{expectedMessage}'", Inspect(exception, query)); + + AssertEx.Equal( + " ..." + Environment.NewLine + + string.Join(Environment.NewLine, Enumerable.Repeat($" at Program.<
$>g__F|0_1(Int64 x) in {FeaturesResources.Query}:line 7", 31)) + Environment.NewLine + + $" at Program.<
$>g__Find|0_0(Compilation compilation)+MoveNext() in Query:line 4" + Environment.NewLine, + exception.StackTrace.JoinText()); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public async Task Exception() + { + using var workspace = TestWorkspace.Create(""" + + + + public class C + { + } + + + + """, composition: FeaturesTestCompositions.Features); + + var solution = workspace.CurrentSolution; + + var service = solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); + + var query = """ + static IEnumerable Find(Compilation compilation) + { + return new[] { (ISymbol)null }.Select(x => + { + return F(x); + }); + } + + static ISymbol F(ISymbol s) + { + var x = s.ToString(); + return s; + } + """; + + var exceptions = new List(); + var observer = new MockSemanticSearchResultsObserver() + { + OnUserCodeExceptionImpl = exceptions.Add + }; + + var traceSource = new TraceSource("test"); + var options = workspace.GlobalOptions.GetClassificationOptionsProvider(); + + var result = await service.ExecuteQueryAsync(solution, query, s_referenceAssembliesDir, observer, options, traceSource, CancellationToken.None); + var expectedMessage = new NullReferenceException().Message; + AssertEx.Equal(string.Format(FeaturesResources.Semantic_search_query_terminated_with_exception, "CSharpAssembly1", expectedMessage), result.ErrorMessage); + + var exception = exceptions.Single(); + AssertEx.Equal($"CSharpAssembly1: [190..190) 'var x = s.ToString();': NullReferenceException: '{expectedMessage}'", Inspect(exception, query)); + AssertEx.Equal( + $" at Program.<
$>g__F|0_1(ISymbol s) in {FeaturesResources.Query}:line 11" + Environment.NewLine + + $" at Program.<>c.<
$>b__0_2(ISymbol x) in {FeaturesResources.Query}:line 5" + Environment.NewLine + + $" at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()" + Environment.NewLine, + exception.StackTrace.JoinText()); + } +} diff --git a/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs b/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs new file mode 100644 index 0000000000000..be7cf4b27cc8a --- /dev/null +++ b/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.SemanticSearch; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SemanticSearch; + +internal class MockSemanticSearchResultsObserver : ISemanticSearchResultsObserver +{ + public Action? OnDefinitionFoundImpl { get; set; } + public Action? OnUserCodeExceptionImpl { get; set; } + public Action>? OnCompilationFailureImpl { get; set; } + public Action? ItemsCompletedImpl { get; set; } + public Action? AddItemsImpl { get; set; } + + public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) + { + AddItemsImpl?.Invoke(itemCount); + return ValueTaskFactory.CompletedTask; + } + + public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) + { + ItemsCompletedImpl?.Invoke(itemCount); + return ValueTaskFactory.CompletedTask; + } + + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken) + { + OnDefinitionFoundImpl?.Invoke(definition); + return ValueTaskFactory.CompletedTask; + } + + public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) + { + OnUserCodeExceptionImpl?.Invoke(exception); + return ValueTaskFactory.CompletedTask; + } + + public ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) + { + OnCompilationFailureImpl?.Invoke(errors); + return ValueTaskFactory.CompletedTask; + } +} + diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs index 1cf7f7b7d46e5..fd88a578f05cf 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/AbstractMoveTypeService.MoveTypeEditor.cs @@ -125,7 +125,7 @@ private async Task AddNewDocumentWithSingleTypeDeclarationAsync(Docume // attributes from the containing partial types. We don't want to create // duplicate attributes on things. AddPartialModifiersToTypeChain( - documentEditor, removeAttributesAndComments: true, removeTypeInheritance: true); + documentEditor, removeAttributesAndComments: true, removeTypeInheritance: true, removePrimaryConstructor: true); // remove things that are not being moved, from the forked document. var membersToRemove = GetMembersToRemove(root); @@ -193,7 +193,7 @@ private async Task RemoveTypeFromSourceDocumentAsync(Document sourceDo // However, keep all the attributes on these types as theses are the // original attributes and we don't want to mess with them. AddPartialModifiersToTypeChain(documentEditor, - removeAttributesAndComments: false, removeTypeInheritance: false); + removeAttributesAndComments: false, removeTypeInheritance: false, removePrimaryConstructor: false); documentEditor.RemoveNode(State.TypeNode, SyntaxRemoveOptions.KeepUnbalancedDirectives); var updatedDocument = documentEditor.GetChangedDocument(); @@ -258,7 +258,8 @@ TMemberDeclarationSyntax or private void AddPartialModifiersToTypeChain( DocumentEditor documentEditor, bool removeAttributesAndComments, - bool removeTypeInheritance) + bool removeTypeInheritance, + bool removePrimaryConstructor) { var semanticFacts = State.SemanticDocument.Document.GetRequiredLanguageService(); var typeChain = State.TypeNode.Ancestors().OfType(); @@ -283,6 +284,11 @@ private void AddPartialModifiersToTypeChain( { documentEditor.RemoveAllTypeInheritance(node); } + + if (removePrimaryConstructor) + { + documentEditor.RemovePrimaryConstructor(node); + } } documentEditor.ReplaceNode(State.TypeNode, diff --git a/src/Features/Core/Portable/Common/UpdatedEventArgs.cs b/src/Features/Core/Portable/Common/UpdatedEventArgs.cs deleted file mode 100644 index a3bd810e1cffa..0000000000000 --- a/src/Features/Core/Portable/Common/UpdatedEventArgs.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.CodeAnalysis.Common; - -internal class UpdatedEventArgs(object id, Workspace workspace, ProjectId? projectId, DocumentId? documentId) : EventArgs -{ - /// - /// The identity of update group. - /// - public object Id { get; } = id; - - /// - /// this update is associated with. - /// - public Workspace Workspace { get; } = workspace; - - /// - /// this update is associated with, or . - /// - public ProjectId? ProjectId { get; } = projectId; - - /// - /// this update is associated with, or . - /// - public DocumentId? DocumentId { get; } = documentId; -} diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs index 0351f0944b2cb..e8ed717a8bf9d 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs @@ -94,7 +94,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) protected abstract IEnumerable GetExistingTopLevelAttributeValues(TSyntax syntax, string tagName, string attributeName); - protected abstract IEnumerable GetKeywordNames(); + protected abstract ImmutableArray GetKeywordNames(); /// /// A temporarily hack that should be removed once/if https://github.com/dotnet/roslyn/issues/53092 is fixed. diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs index 6a3062fe92f1b..6f7d9e28004e5 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs @@ -206,12 +206,30 @@ internal sealed override async Task GetDescriptionWorkerA var name = SymbolCompletionItem.GetSymbolName(item); var kind = SymbolCompletionItem.GetKind(item); var isGeneric = SymbolCompletionItem.GetSymbolIsGeneric(item); - var relatedDocumentIds = document.Project.Solution.GetRelatedDocumentIds(document.Id); var typeConvertibilityCache = new Dictionary(SymbolEqualityComparer.Default); + // First try with the document we're currently within. + var description = await TryGetDescriptionAsync(document.Id).ConfigureAwait(false); + if (description != null) + return description; + + // If that didn't work, see about any related documents. + var relatedDocumentIds = document.Project.Solution.GetRelatedDocumentIds(document.Id); foreach (var relatedId in relatedDocumentIds) { - var relatedDocument = document.Project.Solution.GetRequiredDocument(relatedId); + if (relatedId == document.Id) + continue; + + description = await TryGetDescriptionAsync(relatedId).ConfigureAwait(false); + if (description != null) + return description; + } + + return CompletionDescription.Empty; + + async Task TryGetDescriptionAsync(DocumentId documentId) + { + var relatedDocument = document.Project.Solution.GetRequiredDocument(documentId); var context = await Utilities.CreateSyntaxContextWithExistingSpeculativeModelAsync(relatedDocument, position, cancellationToken).ConfigureAwait(false) as TSyntaxContext; Contract.ThrowIfNull(context); var symbols = await TryGetSymbolsForContextAsync(completionContext: null, context, options, cancellationToken).ConfigureAwait(false); @@ -235,9 +253,9 @@ internal sealed override async Task GetDescriptionWorkerA return await SymbolCompletionItem.GetDescriptionAsync(item, bestSymbols.SelectAsArray(t => t.Symbol), document, context.SemanticModel, displayOptions, cancellationToken).ConfigureAwait(false); } } - } - return CompletionDescription.Empty; + return null; + } static bool SymbolMatches(SymbolAndSelectionInfo info, string? name, SymbolKind? kind, bool isGeneric) { diff --git a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs index 1f3c651245e43..0990ee2947f07 100644 --- a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Tags; namespace Microsoft.CodeAnalysis.Completion.Providers; @@ -56,6 +57,10 @@ private static CompletionItem CreateWorker( AddSupportedPlatforms(builder, supportedPlatforms); symbolEncoder(symbols, builder); + tags = tags.NullToEmpty(); + if (symbols.All(symbol => symbol.IsObsolete()) && !tags.Contains(WellKnownTags.Deprecated)) + tags = tags.Add(WellKnownTags.Deprecated); + var firstSymbol = symbols[0]; var item = CommonCompletionItem.Create( displayText: displayText, diff --git a/src/Features/Core/Portable/Copilot/Extensions.cs b/src/Features/Core/Portable/Copilot/Extensions.cs index 68b39f20e2021..344aed3654abf 100644 --- a/src/Features/Core/Portable/Copilot/Extensions.cs +++ b/src/Features/Core/Portable/Copilot/Extensions.cs @@ -13,27 +13,17 @@ namespace Microsoft.CodeAnalysis.Copilot; internal static class Extensions { - public static async Task> GetCachedCopilotDiagnosticsAsync(this TextDocument document, TextSpan span, CancellationToken cancellationToken) - { - var diagnostics = await document.GetCachedCopilotDiagnosticsAsync(cancellationToken).ConfigureAwait(false); - if (diagnostics.IsEmpty) - return []; - - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - return diagnostics.WhereAsArray(diagnostic => span.IntersectsWith(diagnostic.DataLocation.UnmappedFileSpan.GetClampedTextSpan(text))); - } - - public static async Task> GetCachedCopilotDiagnosticsAsync(this TextDocument document, CancellationToken cancellationToken) + public static async Task> GetCachedCopilotDiagnosticsAsync(this TextDocument document, TextSpan? span, CancellationToken cancellationToken) { if (document is not Document sourceDocument) - return ImmutableArray.Empty; + return []; var copilotCodeAnalysisService = sourceDocument.GetLanguageService(); if (copilotCodeAnalysisService is null) - return ImmutableArray.Empty; + return []; var promptTitles = await copilotCodeAnalysisService.GetAvailablePromptTitlesAsync(sourceDocument, cancellationToken).ConfigureAwait(false); - var copilotDiagnostics = await copilotCodeAnalysisService.GetCachedDocumentDiagnosticsAsync(sourceDocument, promptTitles, cancellationToken).ConfigureAwait(false); + var copilotDiagnostics = await copilotCodeAnalysisService.GetCachedDocumentDiagnosticsAsync(sourceDocument, span, promptTitles, cancellationToken).ConfigureAwait(false); return copilotDiagnostics.SelectAsArray(d => DiagnosticData.Create(d, sourceDocument)); } } diff --git a/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs index 5d3931a773cf2..4aaa20b3c6c17 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs @@ -61,7 +61,7 @@ internal interface ICopilotCodeAnalysisService : ILanguageService /// /// A prompt's title serves as the ID of the prompt, which can be used to selectively trigger analysis and retrive cached results. /// - Task> GetCachedDocumentDiagnosticsAsync(Document document, ImmutableArray promptTitles, CancellationToken cancellationToken); + Task> GetCachedDocumentDiagnosticsAsync(Document document, TextSpan? span, ImmutableArray promptTitles, CancellationToken cancellationToken); /// /// Method to start a Copilot refinement session on top of the changes between the given diff --git a/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs b/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs deleted file mode 100644 index 2e7cdd0704fde..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs +++ /dev/null @@ -1,186 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.Collections; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -/// -/// Diagnostic update source for reporting workspace host specific diagnostics, -/// which may not be related to any given project/document in the solution. -/// For example, these include diagnostics generated for exceptions from third party analyzers. -/// -internal abstract class AbstractHostDiagnosticUpdateSource : IDiagnosticUpdateSource -{ - private ImmutableDictionary> _analyzerHostDiagnosticsMap = - ImmutableDictionary>.Empty; - - public abstract Workspace Workspace { get; } - - public event EventHandler>? DiagnosticsUpdated; - public event EventHandler DiagnosticsCleared { add { } remove { } } - - public void RaiseDiagnosticsUpdated(ImmutableArray args) - { - if (!args.IsEmpty) - DiagnosticsUpdated?.Invoke(this, args); - } - - public void ReportAnalyzerDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, ProjectId? projectId) - { - // check whether we are reporting project specific diagnostic or workspace wide diagnostic - var solution = Workspace.CurrentSolution; - var project = projectId != null ? solution.GetProject(projectId) : null; - - // check whether project the diagnostic belong to still exist - if (projectId != null && project == null) - { - // project the diagnostic belong to already removed from the solution. - // ignore the diagnostic - return; - } - - ReportAnalyzerDiagnostic(analyzer, DiagnosticData.Create(solution, diagnostic, project), project); - } - - public void ReportAnalyzerDiagnostic(DiagnosticAnalyzer analyzer, DiagnosticData diagnosticData, Project? project) - { - var raiseDiagnosticsUpdated = true; - - var dxs = ImmutableInterlocked.AddOrUpdate(ref _analyzerHostDiagnosticsMap, - analyzer, - [diagnosticData], - (a, existing) => - { - var newDiags = existing.Add(diagnosticData); - raiseDiagnosticsUpdated = newDiags.Count > existing.Count; - return newDiags; - }); - - if (raiseDiagnosticsUpdated) - { - RaiseDiagnosticsUpdated([MakeCreatedArgs(analyzer, dxs, project)]); - } - } - - public void ClearAnalyzerReferenceDiagnostics(AnalyzerFileReference analyzerReference, string language, ProjectId projectId) - { - // Perf: if we don't have any diagnostics at all, just return right away; this avoids loading the analyzers - // which may have not been loaded if you didn't do too much in your session. - if (_analyzerHostDiagnosticsMap.Count == 0) - return; - - using var argsBuilder = TemporaryArray.Empty; - var analyzers = analyzerReference.GetAnalyzers(language); - AddArgsToClearAnalyzerDiagnostics(ref argsBuilder.AsRef(), analyzers, projectId); - RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - } - - public void AddArgsToClearAnalyzerDiagnostics(ref TemporaryArray builder, ImmutableArray analyzers, ProjectId projectId) - { - foreach (var analyzer in analyzers) - { - AddArgsToClearAnalyzerDiagnostics(ref builder, analyzer, projectId); - } - } - - public void AddArgsToClearAnalyzerDiagnostics(ref TemporaryArray builder, ProjectId projectId) - { - foreach (var (analyzer, _) in _analyzerHostDiagnosticsMap) - { - AddArgsToClearAnalyzerDiagnostics(ref builder, analyzer, projectId); - } - } - - private void AddArgsToClearAnalyzerDiagnostics(ref TemporaryArray builder, DiagnosticAnalyzer analyzer, ProjectId projectId) - { - if (!_analyzerHostDiagnosticsMap.TryGetValue(analyzer, out var existing)) - { - return; - } - - // Check if analyzer is shared by analyzer references from different projects. - var sharedAnalyzer = existing.Contains(d => d.ProjectId != null && d.ProjectId != projectId); - if (sharedAnalyzer) - { - var newDiags = existing.Where(d => d.ProjectId != projectId).ToImmutableHashSet(); - if (newDiags.Count < existing.Count && - ImmutableInterlocked.TryUpdate(ref _analyzerHostDiagnosticsMap, analyzer, newDiags, existing)) - { - var project = Workspace.CurrentSolution.GetProject(projectId); - builder.Add(MakeRemovedArgs(analyzer, project)); - } - } - else if (ImmutableInterlocked.TryRemove(ref _analyzerHostDiagnosticsMap, analyzer, out existing)) - { - var project = Workspace.CurrentSolution.GetProject(projectId); - builder.Add(MakeRemovedArgs(analyzer, project)); - - if (existing.Any(d => d.ProjectId == null)) - { - builder.Add(MakeRemovedArgs(analyzer, project: null)); - } - } - } - - private DiagnosticsUpdatedArgs MakeCreatedArgs(DiagnosticAnalyzer analyzer, ImmutableHashSet items, Project? project) - { - return DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(analyzer, project), Workspace, project?.Solution, project?.Id, documentId: null, diagnostics: items.ToImmutableArray()); - } - - private DiagnosticsUpdatedArgs MakeRemovedArgs(DiagnosticAnalyzer analyzer, Project? project) - { - return DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(analyzer, project), Workspace, project?.Solution, project?.Id, documentId: null); - } - - private HostArgsId CreateId(DiagnosticAnalyzer analyzer, Project? project) => new(this, analyzer, project?.Id); - - internal TestAccessor GetTestAccessor() - => new(this); - - internal readonly struct TestAccessor(AbstractHostDiagnosticUpdateSource abstractHostDiagnosticUpdateSource) - { - private readonly AbstractHostDiagnosticUpdateSource _abstractHostDiagnosticUpdateSource = abstractHostDiagnosticUpdateSource; - - internal ImmutableArray GetReportedDiagnostics() - => _abstractHostDiagnosticUpdateSource._analyzerHostDiagnosticsMap.Values.Flatten().ToImmutableArray(); - - internal ImmutableHashSet GetReportedDiagnostics(DiagnosticAnalyzer analyzer) - { - if (!_abstractHostDiagnosticUpdateSource._analyzerHostDiagnosticsMap.TryGetValue(analyzer, out var diagnostics)) - { - diagnostics = []; - } - - return diagnostics; - } - } - - private sealed class HostArgsId(AbstractHostDiagnosticUpdateSource source, DiagnosticAnalyzer analyzer, ProjectId? projectId) : AnalyzerUpdateArgsId(analyzer) - { - private readonly AbstractHostDiagnosticUpdateSource _source = source; - private readonly ProjectId? _projectId = projectId; - - public override bool Equals(object? obj) - { - if (obj is not HostArgsId other) - { - return false; - } - - return _source == other._source && _projectId == other._projectId && base.Equals(obj); - } - - public override int GetHashCode() - => Hash.Combine(_source.GetHashCode(), Hash.Combine(_projectId == null ? 1 : _projectId.GetHashCode(), base.GetHashCode())); - } -} diff --git a/src/Features/Core/Portable/Diagnostics/AnalyzerUpdateArgsId.cs b/src/Features/Core/Portable/Diagnostics/AnalyzerUpdateArgsId.cs deleted file mode 100644 index bcc22fc1e1730..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/AnalyzerUpdateArgsId.cs +++ /dev/null @@ -1,22 +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.Common; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -/// -/// Base type of a type that is used as for live diagnostic -/// -internal class AnalyzerUpdateArgsId : BuildToolId.Base, ISupportLiveUpdate -{ - public DiagnosticAnalyzer Analyzer => _Field1!; - - protected AnalyzerUpdateArgsId(DiagnosticAnalyzer analyzer) - : base(analyzer) - { - } - - public override string BuildTool => Analyzer.GetAnalyzerAssemblyName(); -} diff --git a/src/Features/Core/Portable/Diagnostics/BuildToolId.cs b/src/Features/Core/Portable/Diagnostics/BuildToolId.cs deleted file mode 100644 index 2b282e4382a9a..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/BuildToolId.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 Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -/// -/// Support ErrorSource information. -/// -internal abstract class BuildToolId -{ - public abstract string BuildTool { get; } - - internal abstract class Base(T? field) : BuildToolId - { - protected readonly T? _Field1 = field; - - public override bool Equals(object? obj) - { - if (obj is not Base other) - { - return false; - } - - return object.Equals(_Field1, other._Field1); - } - - public override int GetHashCode() - => _Field1?.GetHashCode() ?? 0; - } - - internal abstract class Base(T1? field1, T2? field2) : Base(field2) - { - private readonly T1? _Field2 = field1; - - public override bool Equals(object? obj) - { - if (obj is not Base other) - { - return false; - } - - return object.Equals(_Field2, other._Field2) && base.Equals(other); - } - - public override int GetHashCode() - => Hash.Combine(_Field2?.GetHashCode() ?? 0, base.GetHashCode()); - } -} diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs index 7430819d4befc..10396b0d0208c 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticsUpdatedArgs.cs @@ -5,52 +5,56 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using Microsoft.CodeAnalysis.Common; namespace Microsoft.CodeAnalysis.Diagnostics; -internal sealed class DiagnosticsUpdatedArgs : UpdatedEventArgs +internal sealed class DiagnosticsUpdatedArgs { public readonly DiagnosticsUpdatedKind Kind; public readonly Solution? Solution; + + /// + /// this update is associated with, or . + /// + public readonly ProjectId? ProjectId; + + /// + /// this update is associated with, or . + /// + public readonly DocumentId? DocumentId; public readonly ImmutableArray Diagnostics; private DiagnosticsUpdatedArgs( - object id, - Workspace workspace, Solution? solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray diagnostics, DiagnosticsUpdatedKind kind) - : base(id, workspace, projectId, documentId) { Debug.Assert(diagnostics.All(d => d.ProjectId == projectId && d.DocumentId == documentId)); Debug.Assert(kind != DiagnosticsUpdatedKind.DiagnosticsRemoved || diagnostics.IsEmpty); Solution = solution; + ProjectId = projectId; + DocumentId = documentId; Kind = kind; Diagnostics = diagnostics; } public static DiagnosticsUpdatedArgs DiagnosticsCreated( - object id, - Workspace workspace, - Solution? solution, + Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray diagnostics) { - return new DiagnosticsUpdatedArgs(id, workspace, solution, projectId, documentId, diagnostics, DiagnosticsUpdatedKind.DiagnosticsCreated); + return new DiagnosticsUpdatedArgs(solution, projectId, documentId, diagnostics, DiagnosticsUpdatedKind.DiagnosticsCreated); } public static DiagnosticsUpdatedArgs DiagnosticsRemoved( - object id, - Workspace workspace, Solution? solution, ProjectId? projectId, DocumentId? documentId) { - return new DiagnosticsUpdatedArgs(id, workspace, solution, projectId, documentId, [], DiagnosticsUpdatedKind.DiagnosticsRemoved); + return new DiagnosticsUpdatedArgs(solution, projectId, documentId, [], DiagnosticsUpdatedKind.DiagnosticsRemoved); } } diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 5c85d2e600442..6b7d1a2feef80 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -25,10 +25,9 @@ internal interface IDiagnosticAnalyzerService DiagnosticAnalyzerInfoCache AnalyzerInfoCache { get; } /// - /// Re-analyze given projects and documents. If both and are null, - /// then re-analyzes the entire for the given . + /// Re-analyze all projects and documents. This will cause an LSP diagnostic refresh request to be sent. /// - void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority); + void RequestDiagnosticRefresh(); /// /// Get diagnostics currently stored in the source. returned diagnostic might be out-of-date if solution has changed but analyzer hasn't run for the new solution. diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticUpdateSource.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticUpdateSource.cs deleted file mode 100644 index eb75cb6bd063b..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticUpdateSource.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -/// -/// Implement this to participate in diagnostic service framework as one of diagnostic update source -/// -internal interface IDiagnosticUpdateSource -{ - /// - /// Raise this when new diagnostics are found - /// - event EventHandler> DiagnosticsUpdated; - - /// - /// Raise this when all diagnostics reported from this update source has cleared - /// - event EventHandler DiagnosticsCleared; -} diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticUpdateSourceRegistrationService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticUpdateSourceRegistrationService.cs deleted file mode 100644 index 8d8d929ed5d82..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticUpdateSourceRegistrationService.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. - -#nullable disable - -namespace Microsoft.CodeAnalysis.Diagnostics; - -/// -/// A service that let people to register new IDiagnosticUpdateSource -/// -internal interface IDiagnosticUpdateSourceRegistrationService -{ - /// - /// Register new IDiagnosticUpdateSource - /// - /// Currently, it doesn't support unregister since our event is asynchronous and unregistering source that deal with asynchronous event is not straight forward. - /// - void Register(IDiagnosticUpdateSource source); -} diff --git a/src/Features/Core/Portable/Diagnostics/LiveDiagnosticUpdateArgsId.cs b/src/Features/Core/Portable/Diagnostics/LiveDiagnosticUpdateArgsId.cs deleted file mode 100644 index 3458e3bcf33c0..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/LiveDiagnosticUpdateArgsId.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal class LiveDiagnosticUpdateArgsId : AnalyzerUpdateArgsId -{ - private string? _buildTool; - - public readonly object ProjectOrDocumentId; - public readonly AnalysisKind Kind; - - public LiveDiagnosticUpdateArgsId(DiagnosticAnalyzer analyzer, object projectOrDocumentId, AnalysisKind kind) - : base(analyzer) - { - Contract.ThrowIfNull(projectOrDocumentId); - - ProjectOrDocumentId = projectOrDocumentId; - Kind = kind; - } - - public override string BuildTool => _buildTool ??= ComputeBuildTool(); - - private string ComputeBuildTool() - => Analyzer.IsBuiltInAnalyzer() ? PredefinedBuildTools.Live : Analyzer.GetAnalyzerAssemblyName(); - - public override bool Equals(object? obj) - { - if (obj is not LiveDiagnosticUpdateArgsId other) - { - return false; - } - - return Kind == other.Kind && Equals(ProjectOrDocumentId, other.ProjectOrDocumentId) && base.Equals(obj); - } - - public override int GetHashCode() - => Hash.Combine(ProjectOrDocumentId, Hash.Combine((int)Kind, base.GetHashCode())); -} diff --git a/src/Features/Core/Portable/Diagnostics/PredefinedBuildTools.cs b/src/Features/Core/Portable/Diagnostics/PredefinedBuildTools.cs deleted file mode 100644 index 93de528e384e6..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/PredefinedBuildTools.cs +++ /dev/null @@ -1,13 +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.CodeAnalysis.Diagnostics; - -internal static class PredefinedBuildTools -{ - public static readonly string Build = FeaturesResources.Compiler2; - public static readonly string Live = FeaturesResources.Live; -} diff --git a/src/Features/Core/Portable/Diagnostics/PredefinedDiagnosticProviderNames.cs b/src/Features/Core/Portable/Diagnostics/PredefinedDiagnosticProviderNames.cs deleted file mode 100644 index 52f17c50bec1e..0000000000000 --- a/src/Features/Core/Portable/Diagnostics/PredefinedDiagnosticProviderNames.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -namespace Microsoft.CodeAnalysis.Diagnostics; - -internal static class PredefinedDiagnosticProviderNames -{ -#if false - public const string AddMissingReference = "Add Missing Reference"; - public const string AddUsingOrImport = "Add Using or Import"; - public const string FullyQualify = "Fully Qualify"; - public const string FixIncorrectExitContinue = "Fix Incorrect Exit Continue"; - public const string GenerateConstructor = "Generate Constructor"; - public const string GenerateEndConstruct = "Generate End Construct"; - public const string GenerateEnumMember = "Generate Enum Member"; - public const string GenerateEvent = "Generate Event"; - public const string GenerateVariable = "Generate Variable"; - public const string GenerateMethod = "Generate Method"; - public const string GenerateType = "Generate Type"; - public const string ImplementAbstractClass = "Implement Abstract Class"; - public const string MoveToTopOfFile = "Move To Top Of File"; -#endif - public const string AddUsingOrImport = "Add Using or Import"; - public const string ImplementInterface = "Implement Interface"; - public const string RemoveUnnecessaryCast = "Remove Unnecessary Casts"; - public const string RemoveUnnecessaryImports = "Remove Unnecessary Usings or Imports"; - public const string RenameTracking = "Rename Tracking"; - public const string SimplifyNames = "Simplify Names"; - public const string SpellCheck = "Spell Check"; -} diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index a903b17faaeb8..e2881e3c26ea5 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -511,7 +511,7 @@ public async Task AnalyzeDocumentAsync( Project oldProject, AsyncLazy lazyOldActiveStatementMap, Document newDocument, - ImmutableArray newActiveStatementSpans, + ImmutableArray newActiveStatementSpans, AsyncLazy lazyCapabilities, CancellationToken cancellationToken) { @@ -778,13 +778,12 @@ private void AnalyzeUnchangedActiveMemberBodies( Match topMatch, SourceText newText, ImmutableArray oldActiveStatements, - ImmutableArray newActiveStatementSpans, + ImmutableArray newActiveStatementSpans, [In, Out] ImmutableArray.Builder newActiveStatements, [In, Out] ImmutableArray>.Builder newExceptionRegions, CancellationToken cancellationToken) { Debug.Assert(!newActiveStatementSpans.IsDefault); - Debug.Assert(newActiveStatementSpans.IsEmpty || oldActiveStatements.Length == newActiveStatementSpans.Length); Debug.Assert(oldActiveStatements.Length == newActiveStatements.Count); Debug.Assert(oldActiveStatements.Length == newExceptionRegions.Count); @@ -824,7 +823,7 @@ private void AnalyzeUnchangedActiveMemberBodies( // We seed the method body matching algorithm with tracking spans (unless they were deleted) // to get precise matching. - if (TryGetTrackedStatement(newActiveStatementSpans, i, newText, newBody, out var trackedStatement, out var trackedStatementPart)) + if (TryGetTrackedStatement(newActiveStatementSpans, oldActiveStatements[i].Statement.Id, newText, newBody, out var trackedStatement, out var trackedStatementPart)) { // Adjust for active statements that cover more than the old member span. // For example, C# variable declarators that represent field initializers: @@ -833,7 +832,7 @@ private void AnalyzeUnchangedActiveMemberBodies( // The tracking span might have been moved outside of lambda. // It is not an error to move the statement - we just ignore it. - var oldEnclosingLambdaBody = FindEnclosingLambdaBody(oldBody.EncompassingAncestor, oldMember.FindToken(adjustedOldStatementStart).Parent!); + var oldEnclosingLambdaBody = FindEnclosingLambdaBody(oldBody.EncompassingAncestor, oldBody.EncompassingAncestor.FindToken(adjustedOldStatementStart).Parent!); var newEnclosingLambdaBody = FindEnclosingLambdaBody(newBody.EncompassingAncestor, trackedStatement); if (oldEnclosingLambdaBody == newEnclosingLambdaBody) { @@ -932,7 +931,7 @@ private void AnalyzeChangedMemberBody( bool isMemberReplaced, Match topMatch, ImmutableArray oldActiveStatements, - ImmutableArray newActiveStatementSpans, + ImmutableArray newActiveStatementSpans, EditAndContinueCapabilitiesGrantor capabilities, [Out] ImmutableArray.Builder newActiveStatements, [Out] ImmutableArray>.Builder newExceptionRegions, @@ -948,7 +947,7 @@ private void AnalyzeChangedMemberBody( var diagnosticContext = CreateDiagnosticContext(diagnostics, oldMember, newMember, newDeclaration, newModel, topMatch); - var activeStatementIndices = oldMemberBody?.GetOverlappingActiveStatements(oldActiveStatements)?.ToArray() ?? []; + var activeStatementIndices = oldMemberBody?.GetOverlappingActiveStatementIndices(oldActiveStatements)?.ToArray() ?? []; if (isMemberReplaced && !activeStatementIndices.IsEmpty()) { @@ -1030,7 +1029,7 @@ private void AnalyzeChangedMemberBody( SyntaxNode? trackedNode = null; - if (TryGetTrackedStatement(newActiveStatementSpans, activeStatementIndex, newText, newMemberBody, out var newStatementSyntax, out var _)) + if (TryGetTrackedStatement(newActiveStatementSpans, oldActiveStatements[activeStatementIndex].Statement.Id, newText, newMemberBody, out var newStatementSyntax, out var _)) { var newEnclosingLambdaBody = FindEnclosingLambdaBody(newMemberBody.EncompassingAncestor, newStatementSyntax); @@ -1282,18 +1281,13 @@ private void AnalyzeChangedMemberBody( } } - private static bool TryGetTrackedStatement(ImmutableArray activeStatementSpans, int index, SourceText text, MemberBody body, [NotNullWhen(true)] out SyntaxNode? trackedStatement, out int trackedStatementPart) + private static bool TryGetTrackedStatement(ImmutableArray activeStatementSpans, ActiveStatementId id, SourceText text, MemberBody body, [NotNullWhen(true)] out SyntaxNode? trackedStatement, out int trackedStatementPart) { trackedStatement = null; trackedStatementPart = -1; - // Active statements are not tracked in this document (e.g. the file is closed). - if (activeStatementSpans.IsEmpty) - { - return false; - } - - var trackedLineSpan = activeStatementSpans[index]; + // Active statement span not tracked or tracking span has been lost. + var trackedLineSpan = activeStatementSpans.FirstOrDefault(static (s, id) => s.Id == id, id).LineSpan; if (trackedLineSpan == default) { return false; @@ -2432,7 +2426,7 @@ private async Task> AnalyzeSemanticsAsync( EditScript editScript, IReadOnlyDictionary editMap, ImmutableArray oldActiveStatements, - ImmutableArray newActiveStatementSpans, + ImmutableArray newActiveStatementSpans, IReadOnlyList<(SyntaxNode OldNode, SyntaxNode NewNode, TextSpan DiagnosticSpan)> triviaEdits, Project oldProject, Document? oldDocument, @@ -3016,7 +3010,7 @@ void ReportDeletedMemberActiveStatementsRudeEdits() return; } - var activeStatementIndices = oldBody.GetOverlappingActiveStatements(oldActiveStatements); + var activeStatementIndices = oldBody.GetOverlappingActiveStatementIndices(oldActiveStatements); if (!activeStatementIndices.Any()) { return; @@ -3253,7 +3247,7 @@ IFieldSymbol or // We need to provide syntax map to the compiler if the member is active (see member update above): var isActiveMember = - oldBody.GetOverlappingActiveStatements(oldActiveStatements).Any() || + oldBody.GetOverlappingActiveStatementIndices(oldActiveStatements).Any() || IsStateMachineMethod(oldDeclaration) || ContainsLambda(oldBody); diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatement.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatement.cs index 19701f66dfe94..e30af136737aa 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatement.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatement.cs @@ -18,7 +18,7 @@ internal sealed class ActiveStatement /// /// Ordinal of the active statement within the set of all active statements. /// - public readonly int Ordinal; + public readonly ActiveStatementId Id; /// /// The instruction of the active statement that is being executed. @@ -37,11 +37,9 @@ internal sealed class ActiveStatement /// public readonly ActiveStatementFlags Flags; - public ActiveStatement(int ordinal, ActiveStatementFlags flags, SourceFileSpan span, ManagedInstructionId instructionId) + public ActiveStatement(ActiveStatementId id, ActiveStatementFlags flags, SourceFileSpan span, ManagedInstructionId instructionId) { - Debug.Assert(ordinal >= 0); - - Ordinal = ordinal; + Id = id; Flags = flags; FileSpan = span; InstructionId = instructionId; @@ -54,10 +52,10 @@ public ActiveStatement WithSpan(LinePositionSpan span) => WithFileSpan(FileSpan.WithSpan(span)); public ActiveStatement WithFileSpan(SourceFileSpan span) - => new(Ordinal, Flags, span, InstructionId); + => new(Id, Flags, span, InstructionId); public ActiveStatement WithFlags(ActiveStatementFlags flags) - => new(Ordinal, flags, FileSpan, InstructionId); + => new(Id, flags, FileSpan, InstructionId); public LinePositionSpan Span => FileSpan.Span; @@ -90,5 +88,5 @@ public bool IsStale => (Flags & ActiveStatementFlags.Stale) != 0; private string GetDebuggerDisplay() - => $"{Ordinal}: {Span}"; + => $"{Id}: {Span}"; } diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementId.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementId.cs index ff18daeb358e4..5aa4a993be01e 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatementId.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementId.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.EditAndContinue; -internal readonly struct ActiveStatementId(DocumentId documentId, int ordinal) -{ - public readonly DocumentId DocumentId = documentId; - public readonly int Ordinal = ordinal; -} +[DataContract] +internal readonly record struct ActiveStatementId([property: DataMember(Order = 0)] int Ordinal); + diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementLineSpan.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementLineSpan.cs new file mode 100644 index 0000000000000..dd032bc414339 --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementLineSpan.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.Serialization; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.EditAndContinue; + +/// +/// Represents location of an active statement tracked by the client editor. +/// +/// The corresponding . +/// Line span in the mapped document. +[DataContract] +internal readonly record struct ActiveStatementLineSpan( + [property: DataMember(Order = 0)] ActiveStatementId Id, + [property: DataMember(Order = 1)] LinePositionSpan LineSpan); diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementSpan.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementSpan.cs index 105af64b2ca55..46e64067ad934 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatementSpan.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementSpan.cs @@ -2,67 +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. -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Contracts.EditAndContinue; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EditAndContinue; /// /// Represents a span of an active statement tracked by the client editor. /// +/// The corresponding . +/// Line span in the mapped document. +/// Flags. +/// +/// The id of the unmapped document where the source of the active statement is and from where the statement might be mapped to via #line directive. +/// Null if unknown (not determined yet). +/// [DataContract] -internal readonly struct ActiveStatementSpan : IEquatable -{ - /// - /// The corresponding . - /// - [DataMember(Order = 0)] - public readonly int Ordinal; - - /// - /// Line span in the mapped document. - /// - [DataMember(Order = 1)] - public readonly LinePositionSpan LineSpan; - - /// - /// Flags. - /// - [DataMember(Order = 2)] - public readonly ActiveStatementFlags Flags; - - /// - /// The id of the unmapped document where the source of the active statement is and from where the statement might be mapped to via #line directive. - /// Null if unknown (not determined yet). - /// - [DataMember(Order = 3)] - public readonly DocumentId? UnmappedDocumentId; - - public ActiveStatementSpan(int ordinal, LinePositionSpan lineSpan, ActiveStatementFlags flags, DocumentId? unmappedDocumentId) - { - Debug.Assert(ordinal >= 0); - - Ordinal = ordinal; - LineSpan = lineSpan; - Flags = flags; - UnmappedDocumentId = unmappedDocumentId; - } - - public override bool Equals(object? obj) - => obj is ActiveStatementSpan other && Equals(other); - - public bool Equals(ActiveStatementSpan other) - => Ordinal.Equals(other.Ordinal) && - LineSpan.Equals(other.LineSpan) && - Flags == other.Flags && - UnmappedDocumentId == other.UnmappedDocumentId; - - public override int GetHashCode() - => Hash.Combine(Ordinal, Hash.Combine(LineSpan.GetHashCode(), Hash.Combine(UnmappedDocumentId, (int)Flags))); -} +internal readonly record struct ActiveStatementSpan( + [property: DataMember(Order = 0)] ActiveStatementId Id, + [property: DataMember(Order = 1)] LinePositionSpan LineSpan, + [property: DataMember(Order = 2)] ActiveStatementFlags Flags, + [property: DataMember(Order = 3)] DocumentId? UnmappedDocumentId = null); diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs index f2f503d49ee6d..704b2934917e1 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs @@ -25,8 +25,8 @@ internal sealed class ActiveStatementsMap public static readonly Comparer Comparer = Comparer.Create((x, y) => x.FileSpan.Start.CompareTo(y.FileSpan.Start)); - private static readonly Comparer<(ManagedActiveStatementDebugInfo, SourceFileSpan, int)> s_infoSpanComparer = - Comparer<(ManagedActiveStatementDebugInfo, SourceFileSpan span, int)>.Create((x, y) => x.span.Start.CompareTo(y.span.Start)); + private static readonly Comparer<(ManagedActiveStatementDebugInfo, SourceFileSpan, ActiveStatementId)> s_infoSpanComparer = + Comparer<(ManagedActiveStatementDebugInfo, SourceFileSpan span, ActiveStatementId)>.Create((x, y) => x.span.Start.CompareTo(y.span.Start)); /// /// Groups active statements by document path as listed in the PDB. @@ -60,7 +60,7 @@ public static ActiveStatementsMap Create( ImmutableArray debugInfos, ImmutableDictionary> remapping) { - using var _1 = PooledDictionary>.GetInstance(out var updatedSpansByDocumentPath); + using var _1 = PooledDictionary>.GetInstance(out var updatedSpansByDocumentPath); var ordinal = 0; foreach (var debugInfo in debugInfos) @@ -79,10 +79,10 @@ public static ActiveStatementsMap Create( if (!updatedSpansByDocumentPath.TryGetValue(documentName, out var documentInfos)) { - updatedSpansByDocumentPath.Add(documentName, documentInfos = ArrayBuilder<(ManagedActiveStatementDebugInfo, SourceFileSpan, int)>.GetInstance()); + updatedSpansByDocumentPath.Add(documentName, documentInfos = ArrayBuilder<(ManagedActiveStatementDebugInfo, SourceFileSpan, ActiveStatementId)>.GetInstance()); } - documentInfos.Add((debugInfo, new SourceFileSpan(documentName, baseSpan), ordinal++)); + documentInfos.Add((debugInfo, new SourceFileSpan(documentName, baseSpan), new ActiveStatementId(ordinal++))); } foreach (var (_, infos) in updatedSpansByDocumentPath) @@ -93,7 +93,7 @@ public static ActiveStatementsMap Create( var byDocumentPath = updatedSpansByDocumentPath.ToImmutableDictionary( keySelector: entry => entry.Key, elementSelector: entry => entry.Value.SelectAsArray(item => new ActiveStatement( - ordinal: item.ordinal, + id: item.id, flags: item.info.Flags, span: item.span, instructionId: item.info.ActiveInstruction))); diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index 03cbd749dd828..071b40d5e7440 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -592,6 +592,10 @@ public void DiscardSolutionUpdate() _ = RetrievePendingUpdate(); } + /// + /// Returns s for each document of , + /// or default if not in a break state. + /// public async ValueTask>> GetBaseActiveStatementSpansAsync(Solution solution, ImmutableArray documentIds, CancellationToken cancellationToken) { try @@ -725,7 +729,7 @@ public async ValueTask>> GetB unmappedDocumentId = null; } - return new ActiveStatementSpan(activeStatement.Ordinal, span, activeStatement.Flags, unmappedDocumentId); + return new ActiveStatementSpan(activeStatement.Id, span, activeStatement.Flags, unmappedDocumentId); }); } } @@ -734,6 +738,7 @@ public async ValueTask>> GetB documentIndicesByMappedPath.FreeValues(); activeStatementsInChangedDocuments.FreeValues(); + Debug.Assert(spans.Count == documentIds.Length); return spans.ToImmutable(); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) @@ -802,10 +807,10 @@ public async ValueTask> GetAdjustedActiveSta { foreach (var activeStatement in analysis.ActiveStatements) { - var i = adjustedMappedSpans.FindIndex((s, ordinal) => s.Ordinal == ordinal, activeStatement.Ordinal); + var i = adjustedMappedSpans.FindIndex(static (s, id) => s.Id == id, activeStatement.Id); if (i >= 0) { - adjustedMappedSpans[i] = new ActiveStatementSpan(activeStatement.Ordinal, activeStatement.Span, activeStatement.Flags, unmappedDocumentId); + adjustedMappedSpans[i] = new ActiveStatementSpan(activeStatement.Id, activeStatement.Span, activeStatement.Flags, unmappedDocumentId); } } } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs index 12ef5a0b226ae..a090615ddea6d 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs @@ -16,9 +16,8 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; -[Export(typeof(EditAndContinueDiagnosticUpdateSource))] -[Shared] -internal sealed class EditAndContinueDiagnosticUpdateSource : IDiagnosticUpdateSource +[Export(typeof(EditAndContinueDiagnosticUpdateSource)), Shared] +internal sealed class EditAndContinueDiagnosticUpdateSource { private int _diagnosticsVersion; private bool _previouslyHadDiagnostics; @@ -32,12 +31,7 @@ internal sealed class EditAndContinueDiagnosticUpdateSource : IDiagnosticUpdateS [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public EditAndContinueDiagnosticUpdateSource(IDiagnosticUpdateSourceRegistrationService registrationService) - => registrationService.Register(this); - - // for testing - [SuppressMessage("RoslynDiagnosticsReliability", "RS0034:Exported parts should have [ImportingConstructor]", Justification = "Used incorrectly by tests")] - internal EditAndContinueDiagnosticUpdateSource() + public EditAndContinueDiagnosticUpdateSource() { } @@ -68,7 +62,7 @@ public void ClearDiagnostics(bool isSessionEnding = false) /// /// Reports given set of project or solution level diagnostics. /// - public void ReportDiagnostics(Workspace workspace, Solution solution, ImmutableArray diagnostics, ImmutableArray<(DocumentId, ImmutableArray Diagnostics)> rudeEdits) + public void ReportDiagnostics(Solution solution, ImmutableArray diagnostics, ImmutableArray<(DocumentId, ImmutableArray Diagnostics)> rudeEdits) { RoslynDebug.Assert(solution != null); @@ -99,11 +93,7 @@ public void ReportDiagnostics(Workspace workspace, Solution solution, ImmutableA { foreach (var (documentId, diagnosticData) in documentDiagnostics.GroupBy(static data => data.DocumentId!)) { - var diagnosticGroupId = (this, documentId); - argsBuilder.Add(DiagnosticsUpdatedArgs.DiagnosticsCreated( - diagnosticGroupId, - workspace, solution, documentId.ProjectId, documentId: documentId, @@ -115,11 +105,7 @@ public void ReportDiagnostics(Workspace workspace, Solution solution, ImmutableA { foreach (var (projectId, diagnosticData) in projectDiagnostics.GroupBy(static data => data.ProjectId!)) { - var diagnosticGroupId = (this, projectId); - argsBuilder.Add(DiagnosticsUpdatedArgs.DiagnosticsCreated( - diagnosticGroupId, - workspace, solution, projectId, documentId: null, @@ -129,11 +115,7 @@ public void ReportDiagnostics(Workspace workspace, Solution solution, ImmutableA if (solutionDiagnostics.Length > 0) { - var diagnosticGroupId = this; - argsBuilder.Add(DiagnosticsUpdatedArgs.DiagnosticsCreated( - diagnosticGroupId, - workspace, solution, projectId: null, documentId: null, diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs index fbe2a833404d6..90c463d540cf9 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs @@ -25,7 +25,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; internal sealed class EditAndContinueDocumentAnalysesCache(AsyncLazy baseActiveStatements, AsyncLazy capabilities) { private readonly object _guard = new(); - private readonly Dictionary results, Project baseProject, Document document, ImmutableArray activeStatementSpans)> _analyses = []; + private readonly Dictionary results, Project baseProject, Document document, ImmutableArray activeStatementSpans)> _analyses = []; private readonly AsyncLazy _baseActiveStatements = baseActiveStatements; private readonly AsyncLazy _capabilities = capabilities; @@ -97,7 +97,7 @@ public async ValueTask GetDocumentAnalysisAsync( /// /// Calculates unmapped active statement spans in the from spans provided by . /// - private async Task> GetLatestUnmappedActiveStatementSpansAsync(Document? oldDocument, Document newDocument, ActiveStatementSpanProvider newActiveStatementSpanProvider, CancellationToken cancellationToken) + private async Task> GetLatestUnmappedActiveStatementSpansAsync(Document? oldDocument, Document newDocument, ActiveStatementSpanProvider newActiveStatementSpanProvider, CancellationToken cancellationToken) { if (oldDocument == null) { @@ -118,7 +118,7 @@ private async Task> GetLatestUnmappedActiveStat if (!newLineMappings.Any()) { var newMappedDocumentSpans = await newActiveStatementSpanProvider(newDocument.Id, newDocument.FilePath, cancellationToken).ConfigureAwait(false); - return newMappedDocumentSpans.SelectAsArray(s => s.LineSpan); + return newMappedDocumentSpans.SelectAsArray(static s => new ActiveStatementLineSpan(s.Id, s.LineSpan)); } // The document has #line directives. In order to determine all active statement spans in the document @@ -126,7 +126,7 @@ private async Task> GetLatestUnmappedActiveStat // We retrieve the tracking spans for all such documents and then map them back to this document. using var _1 = PooledDictionary>.GetInstance(out var mappedSpansByDocumentPath); - using var _2 = ArrayBuilder.GetInstance(out var activeStatementSpansBuilder); + using var _2 = ArrayBuilder.GetInstance(out var activeStatementSpansBuilder); var baseActiveStatements = await _baseActiveStatements.GetValueAsync(cancellationToken).ConfigureAwait(false); var analyzer = newDocument.Project.Services.GetRequiredService(); @@ -148,20 +148,20 @@ private async Task> GetLatestUnmappedActiveStat } // all baseline spans are being tracked in their corresponding mapped documents (if a span is deleted it's still tracked as empty): - var newMappedDocumentActiveSpan = newMappedDocumentSpans.GetStatement(oldActiveStatement.Statement.Ordinal); + var newMappedDocumentActiveSpan = newMappedDocumentSpans.Single(static (s, id) => s.Id == id, oldActiveStatement.Statement.Id); Debug.Assert(newMappedDocumentActiveSpan.UnmappedDocumentId == null || newMappedDocumentActiveSpan.UnmappedDocumentId == newDocument.Id); // TODO: optimize var newLineMappingContainingActiveSpan = newLineMappings.FirstOrDefault(mapping => mapping.MappedSpan.Span.Contains(newMappedDocumentActiveSpan.LineSpan)); var unmappedSpan = newLineMappingContainingActiveSpan.MappedSpan.IsValid ? newLineMappingContainingActiveSpan.Span : default; - activeStatementSpansBuilder.Add(unmappedSpan); + activeStatementSpansBuilder.Add(new ActiveStatementLineSpan(newMappedDocumentActiveSpan.Id, unmappedSpan)); } return activeStatementSpansBuilder.ToImmutable(); } - private AsyncLazy GetDocumentAnalysisNoLock(Project baseProject, Document document, ImmutableArray activeStatementSpans) + private AsyncLazy GetDocumentAnalysisNoLock(Project baseProject, Document document, ImmutableArray activeStatementSpans) { // Do not reuse an analysis of the document unless its snasphot is exactly the same as was used to calculate the results. // Note that comparing document snapshots in effect compares the entire solution snapshots (when another document is changed a new solution snapshot is created diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs index 03a68cfd19c34..5e6c4c41282b4 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueAnalyzer.cs @@ -17,7 +17,7 @@ Task AnalyzeDocumentAsync( Project baseProject, AsyncLazy lazyBaseActiveStatements, Document document, - ImmutableArray newActiveStatementSpans, + ImmutableArray newActiveStatementSpans, AsyncLazy lazyCapabilities, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/EditAndContinue/MemberBody.cs b/src/Features/Core/Portable/EditAndContinue/MemberBody.cs index 651177ca09e6b..168225b359a4a 100644 --- a/src/Features/Core/Portable/EditAndContinue/MemberBody.cs +++ b/src/Features/Core/Portable/EditAndContinue/MemberBody.cs @@ -42,7 +42,7 @@ public virtual bool IsExcludedActiveStatementSpanWithinEnvelope(TextSpan span) public SyntaxNode FindStatement(TextSpan span, out int statementPart) => FindStatementAndPartner(span, partnerDeclarationBody: null, out _, out statementPart); - public IEnumerable GetOverlappingActiveStatements(ImmutableArray statements) + public IEnumerable GetOverlappingActiveStatementIndices(ImmutableArray statements) { var envelope = Envelope; diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs index d2b0067d344fb..44e1219eb5c59 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs @@ -50,7 +50,7 @@ public async ValueTask BreakStateOrCapabilitiesChangedAsync(IDiagnosticAnalyzerS } // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: documentsToReanalyze, highPriority: false); + diagnosticService.RequestDiagnosticRefresh(); // clear emit/apply diagnostics reported previously: diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: false); @@ -78,7 +78,7 @@ public async ValueTask EndDebuggingSessionAsync(Solution compileTimeSolution, Ed compileTimeSolution, documentsToReanalyze, designTimeSolution: _workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: designTimeDocumentsToReanalyze, highPriority: false); + diagnosticService.RequestDiagnosticRefresh(); // clear emit/apply diagnostics reported previously: diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: true); @@ -149,10 +149,10 @@ public async ValueTask EndDebuggingSessionAsync(Solution compileTimeSolution, Ed diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: false); // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: rudeEdits.Select(d => d.DocumentId), highPriority: false); + diagnosticService.RequestDiagnosticRefresh(); // report emit/apply diagnostics: - diagnosticUpdateSource.ReportDiagnostics(_workspace, solution, diagnosticData, rudeEdits); + diagnosticUpdateSource.ReportDiagnostics(solution, diagnosticData, rudeEdits); return (moduleUpdates, diagnosticData, rudeEdits, syntaxError); } @@ -188,7 +188,7 @@ public async ValueTask CommitSolutionUpdateAsync(IDiagnosticAnalyzerService diag } // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: documentsToReanalyze, highPriority: false); + diagnosticService.RequestDiagnosticRefresh(); } public async ValueTask DiscardSolutionUpdateAsync(CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs b/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs index e8f40615816a0..4a0c0810683d9 100644 --- a/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs +++ b/src/Features/Core/Portable/EditAndContinue/Utilities/Extensions.cs @@ -35,45 +35,6 @@ public static LinePositionSpan ToLinePositionSpan(this SourceSpan span) public static SourceSpan ToSourceSpan(this LinePositionSpan span) => new(span.Start.Line, span.Start.Character, span.End.Line, span.End.Character); - public static ActiveStatement GetStatement(this ImmutableArray statements, int ordinal) - { - foreach (var item in statements) - { - if (item.Ordinal == ordinal) - { - return item; - } - } - - throw ExceptionUtilities.UnexpectedValue(ordinal); - } - - public static ActiveStatementSpan GetStatement(this ImmutableArray statements, int ordinal) - { - foreach (var item in statements) - { - if (item.Ordinal == ordinal) - { - return item; - } - } - - throw ExceptionUtilities.UnexpectedValue(ordinal); - } - - public static UnmappedActiveStatement GetStatement(this ImmutableArray statements, int ordinal) - { - foreach (var item in statements) - { - if (item.Statement.Ordinal == ordinal) - { - return item; - } - } - - throw ExceptionUtilities.UnexpectedValue(ordinal); - } - /// /// True if the project supports Edit and Continue. /// Only depends on the language of the project and never changes. diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.cs deleted file mode 100644 index 4fd9cf9810021..0000000000000 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.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; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; - -internal interface IVSTypeScriptDiagnosticService -{ - Task> GetPushDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken); - - [Obsolete] - IDisposable RegisterDiagnosticsUpdatedEventHandler(Action action); - - IDisposable RegisterDiagnosticsUpdatedEventHandler(Action> action); -} diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticAnalyzerService.cs index c7c129b9c41ac..2ef151f4f17d7 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticAnalyzerService.cs @@ -20,5 +20,5 @@ internal sealed class VSTypeScriptAnalyzerService(IDiagnosticAnalyzerService ser private readonly IDiagnosticAnalyzerService _service = service; public void Reanalyze(Workspace workspace, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false) - => _service.Reanalyze(workspace, projectIds, documentIds, highPriority); + => _service.RequestDiagnosticRefresh(); } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 24903f5221c28..89a689d9c110b 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -3231,4 +3231,34 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Dismiss + + Symbols + + + Semantic Search + + + Query + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + Semantic search query terminated with exception + + + Semantic search query failed to compile + + + The query does not specify '{0}' method or top-level function + + + Method '{0}' must be static and non-generic + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + + Unable to load type '{0}': '{1}' + \ No newline at end of file diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.GenerateNamedType.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.GenerateNamedType.cs index c1e832b84c41f..a38ed48c9e31e 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.GenerateNamedType.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.GenerateNamedType.cs @@ -236,7 +236,7 @@ private void AddExceptionConstructors(ArrayBuilder members) var exceptionType = _semanticDocument.SemanticModel.Compilation.ExceptionType(); var constructors = exceptionType.InstanceConstructors - .Where(c => c.DeclaredAccessibility is Accessibility.Public or Accessibility.Protected) + .Where(c => c.DeclaredAccessibility is Accessibility.Public or Accessibility.Protected && !c.IsObsolete()) .Select(c => CodeGenerationSymbolFactory.CreateConstructorSymbol( attributes: default, accessibility: c.DeclaredAccessibility, diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index c5575614c4958..f06b4057c313c 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -51,8 +51,8 @@ - - + + diff --git a/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs b/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs index 8322ad19fadbd..11c66bdf46e6b 100644 --- a/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs +++ b/src/Features/Core/Portable/Navigation/AbstractNavigableItemsService.cs @@ -12,21 +12,41 @@ namespace Microsoft.CodeAnalysis.Navigation; -internal class AbstractNavigableItemsService : INavigableItemsService +internal abstract class AbstractNavigableItemsService : INavigableItemsService { public async Task> GetNavigableItemsAsync( Document document, int position, CancellationToken cancellationToken) { var symbolService = document.GetRequiredLanguageService(); - var (symbol, project, _) = await symbolService.GetSymbolProjectAndBoundSpanAsync(document, position, cancellationToken).ConfigureAwait(false); - var solution = project.Solution; - symbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false) ?? symbol; - symbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + // First try with frozen partial semantics. For the common case where no symbols referenced though skeleton + // references are involved, this can be much faster. If that fails, try again, this time allowing skeletons to + // be built. + var symbolAndSolution = + await GetSymbolAsync(document.WithFrozenPartialSemantics(cancellationToken)).ConfigureAwait(false) ?? + await GetSymbolAsync(document).ConfigureAwait(false); + + if (symbolAndSolution is null) + return []; + + var (symbol, solution) = symbolAndSolution.Value; // Try to compute source definitions from symbol. - return symbol != null - ? NavigableItemFactory.GetItemsFromPreferredSourceLocations(solution, symbol, displayTaggedParts: FindUsagesHelpers.GetDisplayParts(symbol), cancellationToken: cancellationToken) - : []; + return NavigableItemFactory.GetItemsFromPreferredSourceLocations(solution, symbol, FindUsagesHelpers.GetDisplayParts(symbol), cancellationToken); + + async Task<(ISymbol symbol, Solution solution)?> GetSymbolAsync(Document document) + { + var (symbol, project, _) = await symbolService.GetSymbolProjectAndBoundSpanAsync(document, position, cancellationToken).ConfigureAwait(false); + + var solution = project.Solution; + + symbol = await SymbolFinder.FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false) ?? symbol; + symbol = await GoToDefinitionFeatureHelpers.TryGetPreferredSymbolAsync(solution, symbol, cancellationToken).ConfigureAwait(false); + + if (symbol is null or IErrorTypeSymbol) + return null; + + return (symbol, solution); + } } } diff --git a/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs b/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs index dd6c5fc18a8c0..164341deca9a8 100644 --- a/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs +++ b/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationService.cs @@ -2,14 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Composition; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Navigation; -internal sealed class DefaultDocumentNavigationService : IDocumentNavigationService +[ExportWorkspaceService(typeof(IDocumentNavigationService), ServiceLayer.Default), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class DefaultDocumentNavigationService() : IDocumentNavigationService { public Task CanNavigateToSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) => SpecializedTasks.False; diff --git a/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationServiceFactory.cs b/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationServiceFactory.cs deleted file mode 100644 index 78e8f43807751..0000000000000 --- a/src/Features/Core/Portable/Navigation/DefaultDocumentNavigationServiceFactory.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. - -#nullable disable - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.Navigation; - -[ExportWorkspaceServiceFactory(typeof(IDocumentNavigationService), ServiceLayer.Default), Shared] -internal sealed class DefaultDocumentNavigationServiceFactory : IWorkspaceServiceFactory -{ - private IDocumentNavigationService _singleton; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DefaultDocumentNavigationServiceFactory() - { - } - - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - { - _singleton ??= new DefaultDocumentNavigationService(); - - return _singleton; - } -} diff --git a/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs b/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs new file mode 100644 index 0000000000000..3194bd7e2095d --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs @@ -0,0 +1,436 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#if NET6_0_OR_GREATER + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Tags; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal abstract partial class AbstractSemanticSearchService : ISemanticSearchService +{ + internal sealed class LoadContext() : AssemblyLoadContext("SemanticSearchLoadContext", isCollectible: true) + { + private readonly AssemblyLoadContext _current = GetLoadContext(typeof(LoadContext).Assembly)!; + + protected override Assembly? Load(AssemblyName assemblyName) + => _current.LoadFromAssemblyName(assemblyName); + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + => IntPtr.Zero; + } + + private static readonly FindReferencesSearchOptions s_findReferencesSearchOptions = new() + { + DisplayAllDefinitions = true, + }; + + private const int StackDisplayDepthLimit = 32; + + protected abstract Compilation CreateCompilation(SourceText query, IEnumerable references, SolutionServices services, out SyntaxTree queryTree, CancellationToken cancellationToken); + + public async Task ExecuteQueryAsync( + Solution solution, + string query, + string referenceAssembliesDir, + ISemanticSearchResultsObserver observer, + OptionsProvider classificationOptions, + TraceSource traceSource, + CancellationToken cancellationToken) + { + try + { + // add progress items - one for compilation, one for emit and one for each project: + var remainingProgressItemCount = 2 + solution.ProjectIds.Count; + await observer.AddItemsAsync(remainingProgressItemCount, cancellationToken).ConfigureAwait(false); + + var metadataService = solution.Services.GetRequiredService(); + var metadataReferences = SemanticSearchUtilities.GetMetadataReferences(metadataService, referenceAssembliesDir); + var queryText = SemanticSearchUtilities.CreateSourceText(query); + var queryCompilation = CreateCompilation(queryText, metadataReferences, solution.Services, out var queryTree, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + // complete compilation progress item: + remainingProgressItemCount--; + await observer.ItemsCompletedAsync(1, cancellationToken).ConfigureAwait(false); + + var emitOptions = new EmitOptions( + debugInformationFormat: DebugInformationFormat.PortablePdb, + instrumentationKinds: [InstrumentationKind.StackOverflowProbing, InstrumentationKind.ModuleCancellation]); + + using var peStream = new MemoryStream(); + using var pdbStream = new MemoryStream(); + + var emitDifferenceTimer = SharedStopwatch.StartNew(); + var emitResult = queryCompilation.Emit(peStream, pdbStream, options: emitOptions, cancellationToken: cancellationToken); + var emitTime = emitDifferenceTimer.Elapsed; + + var executionTime = TimeSpan.Zero; + + cancellationToken.ThrowIfCancellationRequested(); + + // complete compilation progress item: + remainingProgressItemCount--; + await observer.ItemsCompletedAsync(1, cancellationToken).ConfigureAwait(false); + + if (!emitResult.Success) + { + foreach (var diagnostic in emitResult.Diagnostics) + { + if (diagnostic.Severity == DiagnosticSeverity.Error) + { + traceSource.TraceInformation($"Semantic search query compilation failed: {diagnostic}"); + } + } + + await observer.OnCompilationFailureAsync( + emitResult.Diagnostics.SelectAsArray( + d => d.Severity == DiagnosticSeverity.Error, + d => new QueryCompilationError(d.Id, d.GetMessage(), (d.Location.SourceTree == queryTree) ? d.Location.SourceSpan : default)), + cancellationToken).ConfigureAwait(false); + + return CreateResult(FeaturesResources.Semantic_search_query_failed_to_compile); + } + + peStream.Position = 0; + pdbStream.Position = 0; + var loadContext = new LoadContext(); + try + { + var queryAssembly = loadContext.LoadFromStream(peStream, pdbStream); + + var pidType = queryAssembly.GetType("", throwOnError: true); + Contract.ThrowIfNull(pidType); + var moduleCancellationTokenField = pidType.GetField("ModuleCancellationToken", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + Contract.ThrowIfNull(moduleCancellationTokenField); + moduleCancellationTokenField.SetValue(null, cancellationToken); + + if (!TryGetFindMethod(queryAssembly, out var findMethod, out var errorMessage, out var errorMessageArgs)) + { + traceSource.TraceInformation($"Semantic search failed: {errorMessage}"); + return CreateResult(errorMessage, errorMessageArgs); + } + + var executionTimeStopWatch = new Stopwatch(); + + foreach (var project in solution.Projects) + { + cancellationToken.ThrowIfCancellationRequested(); + + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + try + { + executionTimeStopWatch.Start(); + + try + { + var symbols = (IEnumerable?)findMethod.Invoke(null, [compilation]) ?? []; + + foreach (var symbol in symbols) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (symbol != null) + { + executionTimeStopWatch.Stop(); + + try + { + var definitionItem = await symbol.ToClassifiedDefinitionItemAsync( + classificationOptions, solution, s_findReferencesSearchOptions, isPrimary: true, includeHiddenLocations: false, cancellationToken).ConfigureAwait(false); + + await observer.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + // skip symbol + } + + executionTimeStopWatch.Start(); + } + } + } + finally + { + executionTimeStopWatch.Stop(); + executionTime = executionTimeStopWatch.Elapsed; + } + } + catch (Exception e) when (e is not OperationCanceledException) + { + // exception from user code + + if (e is TargetInvocationException { InnerException: { } innerException }) + { + e = innerException; + } + + var (projectName, projectFlavor) = project.State.NameAndFlavor; + projectName ??= project.Name; + var projectDisplay = string.IsNullOrEmpty(projectFlavor) ? projectName : $"{projectName} ({projectFlavor})"; + + FormatStackTrace(e, queryAssembly, out var position, out var stackTraceTaggedText); + var span = queryText.Lines.GetTextSpan(new LinePositionSpan(position, position)); + + var exceptionNameTaggedText = GetExceptionTypeTaggedText(e, compilation); + + await observer.OnUserCodeExceptionAsync(new UserCodeExceptionInfo(projectDisplay, e.Message, exceptionNameTaggedText, stackTraceTaggedText, span), cancellationToken).ConfigureAwait(false); + + traceSource.TraceInformation($"Semantic query execution failed due to user code exception: {e}"); + return CreateResult(FeaturesResources.Semantic_search_query_terminated_with_exception); + } + + // complete project progress item: + remainingProgressItemCount--; + await observer.ItemsCompletedAsync(1, cancellationToken).ConfigureAwait(false); + } + } + finally + { + loadContext.Unload(); + + // complete the remaining items (in case the search gets interrupted) + if (remainingProgressItemCount > 0) + { + await observer.ItemsCompletedAsync(remainingProgressItemCount, cancellationToken).ConfigureAwait(false); + } + } + + return CreateResult(errorMessage: null); + + ExecuteQueryResult CreateResult(string? errorMessage, params string[]? args) + => new(errorMessage, args, emitTime, executionTime); + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) + { + throw ExceptionUtilities.Unreachable(); + } + } + + private static ImmutableArray GetExceptionTypeTaggedText(Exception e, Compilation compilation) + => e.GetType().FullName is { } exceptionTypeName + ? compilation.GetTypeByMetadataName(exceptionTypeName) is { } exceptionTypeSymbol + ? exceptionTypeSymbol.ToDisplayParts(SymbolDisplayFormat.MinimallyQualifiedFormat).ToTaggedText() + : [new TaggedText(WellKnownTags.Class, exceptionTypeName)] + : [new TaggedText(WellKnownTags.Class, nameof(Exception))]; + + private static void FormatStackTrace(Exception e, Assembly queryAssembly, out LinePosition position, out ImmutableArray formattedTrace) + { + position = default; + + try + { + var trace = new StackTrace(e, fNeedFileInfo: true); + var frames = trace.GetFrames(); + var displayFrames = frames; + var skippedFrameCount = 0; + + try + { + var hostAssembly = typeof(AbstractSemanticSearchService).Assembly; + var displayFramesEnd = frames.Length; + var foundPosition = false; + for (var i = 0; i < frames.Length; i++) + { + var frame = frames[i]; + + if (frame.GetMethod() is { } method) + { + var frameAssembly = method.DeclaringType?.Assembly; + if (frameAssembly == hostAssembly) + { + displayFramesEnd = i; + break; + } + + if (!foundPosition && + frameAssembly == queryAssembly && + frame.GetFileName() is { } fileName && + frame.GetFileLineNumber() is > 0 and var line && + frame.GetFileColumnNumber() is > 0 and var column) + { + position = new LinePosition(line - 1, column - 1); + foundPosition = true; + } + } + } + + // display last StackDisplayDepthLimit frames preceding the host frame: + skippedFrameCount = Math.Max(0, displayFramesEnd - StackDisplayDepthLimit); + displayFrames = frames[skippedFrameCount..displayFramesEnd]; + } + catch + { + // nop + } + + formattedTrace = + [ + new TaggedText(tag: TextTags.Text, (skippedFrameCount > 0 ? " ..." + Environment.NewLine : "") + GetStackTraceText(displayFrames)) + ]; + } + catch + { + formattedTrace = []; + } + } + + private static string GetStackTraceText(IEnumerable frames) + { +#if NET8_0_OR_GREATER + return new StackTrace(frames).ToString(); +#else + var builder = new StringBuilder(); + foreach (var frame in frames) + { + builder.Append(new StackTrace(frame).ToString()); + } + + return builder.ToString(); +#endif + } + + private static bool TryGetFindMethod(Assembly queryAssembly, [NotNullWhen(true)] out MethodInfo? method, out string? error, out string[]? errorMessageArgs) + { + // TODO: Use Compilation APIs to find the method + + method = null; + error = null; + errorMessageArgs = null; + + Type? program; + try + { + program = queryAssembly.GetType(WellKnownMemberNames.TopLevelStatementsEntryPointTypeName, throwOnError: false); + } + catch (Exception e) + { + error = FeaturesResources.Unable_to_load_type_0_1; + errorMessageArgs = [WellKnownMemberNames.TopLevelStatementsEntryPointTypeName, e.Message]; + return false; + } + + if (program != null) + { + try + { + method = GetFindMethod(program, allowLocalFunction: true, ref error); + } + catch + { + } + + if (method != null) + { + return true; + } + } + + Type[] types; + try + { + types = queryAssembly.GetTypes(); + } + catch (TypeLoadException e) + { + error = FeaturesResources.Unable_to_load_type_0_1; + errorMessageArgs = [e.TypeName, e.Message]; + method = null; + return false; + } + + foreach (var type in types) + { + method = GetFindMethod(type, allowLocalFunction: false, ref error); + if (method != null) + { + return true; + } + } + + error ??= string.Format(FeaturesResources.The_query_does_not_specify_0_method_or_top_level_function, SemanticSearchUtilities.FindMethodName); + return false; + } + + private static MethodInfo? GetFindMethod(Type type, bool allowLocalFunction, ref string? error) + { + try + { + var candidates = new ArrayBuilder(); + + foreach (var candidate in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) + { + if (candidate.Name == SemanticSearchUtilities.FindMethodName || + allowLocalFunction && candidate.Name.StartsWith($"<{WellKnownMemberNames.TopLevelStatementsEntryPointMethodName}>g__{SemanticSearchUtilities.FindMethodName}|")) + { + candidates.Add(candidate); + } + } + + if (candidates is []) + { + error = string.Format(FeaturesResources.The_query_does_not_specify_0_method_or_top_level_function, SemanticSearchUtilities.FindMethodName); + return null; + } + + candidates.RemoveAll(candidate => candidate.IsGenericMethod || !candidate.IsStatic); + if (candidates is []) + { + error = string.Format(FeaturesResources.Method_0_must_be_static_and_non_generic, SemanticSearchUtilities.FindMethodName); + return null; + } + + candidates.RemoveAll(candidate => !( + typeof(IEnumerable).IsAssignableFrom(candidate.ReturnType) && + candidate.GetParameters() is [{ ParameterType: var paramType }] && + typeof(Compilation).IsAssignableFrom(paramType))); + + if (candidates is []) + { + error = string.Format(FeaturesResources.Method_0_must_have_a_single_parameter_of_type_1_and_return_2, SemanticSearchUtilities.FindMethodName, nameof(Compilation)); + return null; + } + + Debug.Assert(candidates.Count == 1); + return candidates[0]; + } + catch (Exception e) + { + error = e.Message; + return null; + } + } +} +#endif diff --git a/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs b/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs new file mode 100644 index 0000000000000..eff18ccc9528c --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +/// +/// The result of Semantic Search query execution. +/// +/// An error message if the execution failed. +/// +/// Arguments to be substituted to . +/// Use when the values may contain PII that needs to be obscured in telemetry. +/// Otherwise, should contain the formatted message. +/// +/// Time it took to emit the query compilation. +/// Time it took to execute the query. +[DataContract] +internal readonly record struct ExecuteQueryResult( + [property: DataMember(Order = 0)] string? ErrorMessage, + [property: DataMember(Order = 1)] string[]? ErrorMessageArgs = null, + [property: DataMember(Order = 2)] TimeSpan EmitTime = default, + [property: DataMember(Order = 3)] TimeSpan ExecutionTime = default); diff --git a/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs b/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs new file mode 100644 index 0000000000000..f967dc4ec9eac --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Remote; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal interface IRemoteSemanticSearchService +{ + internal interface ICallback + { + ValueTask OnDefinitionFoundAsync(RemoteServiceCallbackId callbackId, SerializableDefinitionItem definition, CancellationToken cancellationToken); + ValueTask OnUserCodeExceptionAsync(RemoteServiceCallbackId callbackId, UserCodeExceptionInfo exception, CancellationToken cancellationToken); + ValueTask OnCompilationFailureAsync(RemoteServiceCallbackId callbackId, ImmutableArray errors, CancellationToken cancellationToken); + ValueTask GetClassificationOptionsAsync(RemoteServiceCallbackId callbackId, string language, CancellationToken cancellationToken); + ValueTask AddItemsAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken); + ValueTask ItemsCompletedAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken); + } + + ValueTask ExecuteQueryAsync(Checksum solutionChecksum, RemoteServiceCallbackId callbackId, string language, string query, string referenceAssembliesDir, CancellationToken cancellationToken); +} + +internal static class RemoteSemanticSearchServiceProxy +{ + [ExportRemoteServiceCallbackDispatcher(typeof(IRemoteSemanticSearchService)), Shared] + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class CallbackDispatcher() : RemoteServiceCallbackDispatcher, IRemoteSemanticSearchService.ICallback + { + public ValueTask OnDefinitionFoundAsync(RemoteServiceCallbackId callbackId, SerializableDefinitionItem definition, CancellationToken cancellationToken) + => ((ServerCallback)GetCallback(callbackId)).OnDefinitionFoundAsync(definition, cancellationToken); + + public ValueTask OnUserCodeExceptionAsync(RemoteServiceCallbackId callbackId, UserCodeExceptionInfo exception, CancellationToken cancellationToken) + => ((ServerCallback)GetCallback(callbackId)).OnUserCodeExceptionAsync(exception, cancellationToken); + + public ValueTask OnCompilationFailureAsync(RemoteServiceCallbackId callbackId, ImmutableArray errors, CancellationToken cancellationToken) + => ((ServerCallback)GetCallback(callbackId)).OnCompilationFailureAsync(errors, cancellationToken); + + public ValueTask AddItemsAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken) + => ((ServerCallback)GetCallback(callbackId)).AddItemsAsync(itemCount, cancellationToken); + + public ValueTask ItemsCompletedAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken) + => ((ServerCallback)GetCallback(callbackId)).ItemsCompletedAsync(itemCount, cancellationToken); + + public ValueTask GetClassificationOptionsAsync(RemoteServiceCallbackId callbackId, string language, CancellationToken cancellationToken) + => ((ServerCallback)GetCallback(callbackId)).GetClassificationOptionsAsync(language, cancellationToken); + } + + internal sealed class ServerCallback(Solution solution, ISemanticSearchResultsObserver observer, OptionsProvider classificationOptions) + { + public async ValueTask OnDefinitionFoundAsync(SerializableDefinitionItem definition, CancellationToken cancellationToken) + { + try + { + var rehydratedDefinition = await definition.RehydrateAsync(solution, cancellationToken).ConfigureAwait(false); + await observer.OnDefinitionFoundAsync(rehydratedDefinition, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + } + + public async ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) + { + try + { + await observer.OnUserCodeExceptionAsync(exception, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + } + + public async ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) + { + try + { + await observer.OnCompilationFailureAsync(errors, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + } + + public async ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) + { + try + { + await observer.AddItemsAsync(itemCount, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + } + + public async ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) + { + try + { + await observer.ItemsCompletedAsync(itemCount, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + } + } + + public async ValueTask GetClassificationOptionsAsync(string language, CancellationToken cancellationToken) + { + try + { + return await classificationOptions.GetOptionsAsync(solution.Services.GetLanguageServices(language), cancellationToken).ConfigureAwait(false); + } + catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) + { + return ClassificationOptions.Default; + } + } + } + + public static async ValueTask ExecuteQueryAsync(Solution solution, string language, string query, string referenceAssembliesDir, ISemanticSearchResultsObserver results, OptionsProvider classificationOptions, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); + if (client == null) + { + return new ExecuteQueryResult(FeaturesResources.Semantic_search_only_supported_on_net_core); + } + + var serverCallback = new ServerCallback(solution, results, classificationOptions); + + var result = await client.TryInvokeAsync( + solution, + (service, solutionInfo, callbackId, cancellationToken) => service.ExecuteQueryAsync(solutionInfo, callbackId, language, query, referenceAssembliesDir, cancellationToken), + callbackTarget: serverCallback, + cancellationToken).ConfigureAwait(false); + + return result.Value; + } +} diff --git a/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs new file mode 100644 index 0000000000000..078e157b8c391 --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal interface ISemanticSearchResultsObserver +{ + ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken); + ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken); + ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken); + ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken); + ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken); +} + +[DataContract] +internal readonly record struct UserCodeExceptionInfo( + [property: DataMember(Order = 0)] string ProjectDisplayName, + [property: DataMember(Order = 1)] string Message, + [property: DataMember(Order = 2)] ImmutableArray TypeName, + [property: DataMember(Order = 3)] ImmutableArray StackTrace, + [property: DataMember(Order = 4)] TextSpan Span); + +[DataContract] +internal readonly record struct QueryCompilationError( + [property: DataMember(Order = 0)] string Id, + [property: DataMember(Order = 1)] string Message, + [property: DataMember(Order = 2)] TextSpan Span); diff --git a/src/Features/Core/Portable/SemanticSearch/ISemanticSearchService.cs b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchService.cs new file mode 100644 index 0000000000000..a61eccb7c134b --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchService.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal interface ISemanticSearchService : ILanguageService +{ + /// + /// Executes given query against . + /// + /// The solution snapshot. + /// Query (top-level code). + /// Directory that contains refernece assemblies to be used for compilation of the query. + /// Observer of the found symbols. + /// Options to use to classify the textual representation of the found symbols. + /// Cancellation token. + /// Error message on failure. + Task ExecuteQueryAsync( + Solution solution, + string query, + string referenceAssembliesDir, + ISemanticSearchResultsObserver observer, + OptionsProvider classificationOptions, + TraceSource traceSource, + CancellationToken cancellationToken); +} diff --git a/src/Features/Core/Portable/Diagnostics/ISupportLiveUpdate.cs b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchWorkspaceHost.cs similarity index 52% rename from src/Features/Core/Portable/Diagnostics/ISupportLiveUpdate.cs rename to src/Features/Core/Portable/SemanticSearch/ISemanticSearchWorkspaceHost.cs index 3f10d2e011a39..1f6160c58c679 100644 --- a/src/Features/Core/Portable/Diagnostics/ISupportLiveUpdate.cs +++ b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchWorkspaceHost.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.CodeAnalysis.Diagnostics; +namespace Microsoft.CodeAnalysis.SemanticSearch; /// -/// Marker interface to indicate whether given diagnostic args are from live analysis. +/// Provides access to singleton. /// -internal interface ISupportLiveUpdate +internal interface ISemanticSearchWorkspaceHost { + SemanticSearchWorkspace Workspace { get; } } diff --git a/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs b/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs new file mode 100644 index 0000000000000..a989618d1f0c2 --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Tags; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationError error, Document queryDocument) + : DefinitionItem( + tags: + [ + WellKnownTags.Error + ], + displayParts: + [ + new TaggedText(TextTags.Text, error.Id), + new TaggedText(TextTags.Punctuation, ":"), + new TaggedText(TextTags.Space, " "), + new TaggedText(TextTags.Text, error.Message) + ], + nameDisplayParts: [], + sourceSpans: + [ + new DocumentSpan(queryDocument, error.Span) + ], + classifiedSpans: [], + metadataLocations: [], + properties: null, + displayableProperties: null, + displayIfNoReferences: true) +{ + internal override bool IsExternal => false; + + public override Task GetNavigableLocationAsync(Workspace workspace, CancellationToken cancellationToken) + => Task.FromResult(null); +} + diff --git a/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs b/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs new file mode 100644 index 0000000000000..3318f13ed1bf0 --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Tags; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal sealed class SearchExceptionDefinitionItem(string message, ImmutableArray exceptionTypeName, ImmutableArray stackTrace, DocumentSpan documentSpan) + : DefinitionItem( + tags: + [ + WellKnownTags.Error + ], + displayParts: + [ + .. exceptionTypeName, + new TaggedText(TextTags.Punctuation, ":"), + new TaggedText(TextTags.Space, " "), + new TaggedText(TextTags.StringLiteral, message), + new TaggedText(TextTags.Space, Environment.NewLine), + .. stackTrace + ], + nameDisplayParts: exceptionTypeName, + sourceSpans: + [ + documentSpan + ], + classifiedSpans: [], + metadataLocations: [], + properties: null, + displayableProperties: null, + displayIfNoReferences: true) +{ + internal override bool IsExternal => false; + + public override Task GetNavigableLocationAsync(Workspace workspace, CancellationToken cancellationToken) + => Task.FromResult(null); +} + diff --git a/src/Features/Core/Portable/SemanticSearch/SemanticSearchDocumentSupportsFeatureService.cs b/src/Features/Core/Portable/SemanticSearch/SemanticSearchDocumentSupportsFeatureService.cs new file mode 100644 index 0000000000000..bdee370068f02 --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/SemanticSearchDocumentSupportsFeatureService.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +[ExportWorkspaceService(typeof(IDocumentSupportsFeatureService), WorkspaceKind.SemanticSearch), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class SemanticSearchDocumentSupportsFeatureService() : IDocumentSupportsFeatureService +{ + public bool SupportsCodeFixes(Document document) + => SemanticSearchUtilities.IsQueryDocument(document); + + public bool SupportsRefactorings(Document document) + => SemanticSearchUtilities.IsQueryDocument(document); + + public bool SupportsRename(Document document) + => SemanticSearchUtilities.IsQueryDocument(document); + + public bool SupportsNavigationToAnyPosition(Document document) + => SemanticSearchUtilities.IsQueryDocument(document); + + public bool SupportsSemanticSnippets(Document document) + => SemanticSearchUtilities.IsQueryDocument(document); +} diff --git a/src/Features/Core/Portable/SemanticSearch/SemanticSearchProjectConfiguration.cs b/src/Features/Core/Portable/SemanticSearch/SemanticSearchProjectConfiguration.cs new file mode 100644 index 0000000000000..99485a2a7717b --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/SemanticSearchProjectConfiguration.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal sealed class SemanticSearchProjectConfiguration +{ + public required string Language { get; init; } + public required string Query { get; init; } + public required string GlobalUsings { get; init; } + public required string EditorConfig { get; init; } + public required ParseOptions ParseOptions { get; init; } + public required CompilationOptions CompilationOptions { get; init; } +} diff --git a/src/Features/Core/Portable/SemanticSearch/SemanticSearchUtilities.cs b/src/Features/Core/Portable/SemanticSearch/SemanticSearchUtilities.cs new file mode 100644 index 0000000000000..78cb3276b171a --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/SemanticSearchUtilities.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Shared.Extensions; +using System.Linq; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal static class SemanticSearchUtilities +{ + public const string ReferenceAssemblyDirectoryName = "SemanticSearchRefs"; + public const string GlobalUsingsDocumentName = "GlobalUsings"; + public const string ConfigDocumentName = ".editorconfig"; + public const string FindMethodName = "Find"; + + public static readonly string QueryProjectName = FeaturesResources.SemanticSearch; + public static readonly string QueryDocumentName = FeaturesResources.Query; + + private static readonly string s_thisAssemblyDirectory = Path.GetDirectoryName(typeof(SemanticSearchUtilities).Assembly.Location!)!; + public static readonly string ReferenceAssembliesDirectory = Path.Combine(s_thisAssemblyDirectory, ReferenceAssemblyDirectoryName); + + public static List GetMetadataReferences(IMetadataService metadataService, string directory) + { + // TODO: https://github.com/dotnet/roslyn/issues/72585 + + var metadataReferences = new List(); + try + { + foreach (var path in Directory.EnumerateFiles(directory, "*.dll", SearchOption.TopDirectoryOnly)) + { + try + { + metadataReferences.Add(metadataService.GetReference(path, MetadataReferenceProperties.Assembly)); + } + catch + { + continue; + } + } + } + catch (Exception e) when (FatalError.ReportAndCatch(e, ErrorSeverity.Diagnostic)) + { + metadataReferences = []; + } + + return metadataReferences; + } + + public static string GetDocumentFilePath(string language) + => Path.Combine(s_thisAssemblyDirectory, QueryDocumentName + (language == LanguageNames.CSharp ? ".cs" : ".vb")); + + public static string GetConfigDocumentFilePath() + => Path.Combine(s_thisAssemblyDirectory, ConfigDocumentName); + + public static SourceText CreateSourceText(string query) + => SourceText.From(query, Encoding.UTF8, SourceHashAlgorithm.Sha256); + + public static Document GetQueryDocument(Solution solution) + => solution.GetRequiredDocument(GetQueryDocumentId(solution)); + + public static ProjectId GetQueryProjectId(Solution solution) + => solution.ProjectIds.Single(); + + public static Project GetQueryProject(Solution solution) + => solution.Projects.Single(); + + public static DocumentId GetQueryDocumentId(Solution solution) + => GetQueryProject(solution).DocumentIds[0]; + + public static bool IsQueryDocument(Document document) + => GetQueryDocumentId(document.Project.Solution) == document.Id; +} diff --git a/src/Features/Core/Portable/SemanticSearch/SemanticSearchWorkspace.cs b/src/Features/Core/Portable/SemanticSearch/SemanticSearchWorkspace.cs new file mode 100644 index 0000000000000..beeda8030c65c --- /dev/null +++ b/src/Features/Core/Portable/SemanticSearch/SemanticSearchWorkspace.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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal abstract class SemanticSearchWorkspace(HostServices services, SemanticSearchProjectConfiguration config) + : Workspace(services, WorkspaceKind.SemanticSearch) +{ + public override bool CanOpenDocuments + => true; + + public override bool CanApplyChange(ApplyChangesKind feature) + => feature == ApplyChangesKind.ChangeDocument; + + public async Task UpdateQueryDocumentAsync(string? query, CancellationToken cancellationToken) + { + SourceText? newText = null; + + var (_, newSolution) = await SetCurrentSolutionAsync( + useAsync: true, + transformation: oldSolution => + { + if (oldSolution.Projects.Any()) + { + if (query == null) + { + // already have a content, don't reset it to default: + return oldSolution; + } + + newText = SemanticSearchUtilities.CreateSourceText(query); + return oldSolution.WithDocumentText(SemanticSearchUtilities.GetQueryDocumentId(oldSolution), newText); + } + + newText = SemanticSearchUtilities.CreateSourceText(query ?? config.Query); + var metadataService = oldSolution.Services.GetRequiredService(); + + return oldSolution + .AddProject(name: SemanticSearchUtilities.QueryProjectName, assemblyName: SemanticSearchUtilities.QueryProjectName, config.Language) + .WithCompilationOptions(config.CompilationOptions) + .WithParseOptions(config.ParseOptions) + .AddMetadataReferences(SemanticSearchUtilities.GetMetadataReferences(metadataService, SemanticSearchUtilities.ReferenceAssembliesDirectory)) + .AddDocument(name: SemanticSearchUtilities.QueryDocumentName, newText, filePath: SemanticSearchUtilities.GetDocumentFilePath(config.Language)).Project + .AddDocument(name: SemanticSearchUtilities.GlobalUsingsDocumentName, SemanticSearchUtilities.CreateSourceText(config.GlobalUsings), filePath: null).Project + .AddAnalyzerConfigDocument(name: SemanticSearchUtilities.ConfigDocumentName, SemanticSearchUtilities.CreateSourceText(config.EditorConfig), filePath: SemanticSearchUtilities.GetConfigDocumentFilePath()).Project.Solution; + }, + changeKind: (oldSolution, newSolution) => + oldSolution.Projects.Any() + ? (WorkspaceChangeKind.DocumentChanged, projectId: null, documentId: SemanticSearchUtilities.GetQueryDocumentId(newSolution)) + : (WorkspaceChangeKind.ProjectAdded, projectId: SemanticSearchUtilities.GetQueryProjectId(newSolution), documentId: null), + onBeforeUpdate: null, + onAfterUpdate: null, + cancellationToken).ConfigureAwait(false); + + var queryDocument = SemanticSearchUtilities.GetQueryDocument(newSolution); + + if (newText != null) + { + ApplyQueryDocumentTextChanged(newText); + } + + return queryDocument; + } + + protected virtual void ApplyQueryDocumentTextChanged(SourceText newText) + { + } +} diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 63516e7fefb3f..23dc84fb88357 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -322,7 +322,7 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Built-in Copilot analysis - Built-in Copilot analysis + Integrovaná analýza Copilot @@ -657,7 +657,7 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Dismiss - Dismiss + Zavřít @@ -1155,6 +1155,16 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Metoda {0} musí vrátit stream, který podporuje operace čtení a hledání. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Různé soubory @@ -1385,6 +1395,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Před kvantifikátorem {x,y} není nic uvedeno. This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group odkaz na nedefinovanou skupinu @@ -2620,6 +2635,26 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Výběr není obsažený v typu. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Tichý @@ -2680,6 +2715,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv V cestě sestavení {0} byl nalezen symbol. + + Symbols + Symbols + + Syntax error Chyba syntaxe @@ -2710,6 +2750,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. Výběr obsahuje volání lokální funkce, aniž by byla deklarovaná. @@ -2765,6 +2810,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Není možné přečíst zdrojový soubor {0} nebo soubor PDB sestavený pro projekt, který tyto soubory obsahuje. Případné změny provedené v tomto souboru během ladění se nepoužijí, dokud se jeho obsah nebude shodovat se sestaveným zdrojem. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Neznámá vlastnost diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 62bad2305aebc..45a6398c4cf84 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -322,7 +322,7 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Built-in Copilot analysis - Built-in Copilot analysis + Integrierte Copilot-Analyse @@ -657,7 +657,7 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Dismiss - Dismiss + Schließen @@ -1155,6 +1155,16 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d "{0}" muss einen Datenstrom zurückgeben, der Lese- und Suchvorgänge unterstützt. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Sonstige Dateien @@ -1385,6 +1395,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Quantifizierer {x,y} nach nichts. This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group Verweis auf nicht definierte Gruppe @@ -2620,6 +2635,26 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Die Auswahl ist nicht in keinem Typ enthalten. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Still @@ -2680,6 +2715,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Das Symbol wurde unter dem Assemblypfad „{0}“ gefunden + + Symbols + Symbols + + Syntax error Syntaxfehler @@ -2710,6 +2750,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. Die Auswahl enthält einen lokalen Funktionsaufruf ohne Deklaration. @@ -2765,6 +2810,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Die Quelldatei "{0}" oder die für das enthaltende Projekt kompilierte PDB-Datei kann nicht gelesen werden. Alle Änderungen, die während des Debuggens an dieser Datei vorgenommen wurden, werden erst angewendet, wenn der Inhalt dem kompilierten Quellcode entspricht. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Unbekannte Eigenschaft diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 73775a7ad045e..71531aa60b319 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -322,7 +322,7 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Built-in Copilot analysis - Built-in Copilot analysis + Análisis integrado de Copilot @@ -372,7 +372,7 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa TODO - TODO + TODO "TODO" is an indication that there is work still to be done. @@ -657,7 +657,7 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Dismiss - Dismiss + Descartar @@ -1155,6 +1155,16 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa {0} debe devolver una secuencia que admita operaciones de lectura y búsqueda. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Archivos varios @@ -1385,6 +1395,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Cuantificador {x,y} después de nada This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group referencia al grupo no definido @@ -2620,6 +2635,26 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us La selección no está contenida dentro de un tipo. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Silencioso @@ -2680,6 +2715,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Símbolo encontrado en la ruta de acceso del ensamblado '{0}' + + Symbols + Symbols + + Syntax error Error de sintaxis @@ -2710,6 +2750,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. La selección contiene una llamada a una función local sin la declaración correspondiente. @@ -2765,6 +2810,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us No se puede leer el archivo de código fuente "{0}" o el PDB compilado para el proyecto que lo contiene. Los cambios realizados en este archivo durante la depuración no se aplicarán hasta que su contenido coincida con el del código fuente compilado. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Propiedad desconocida diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index b079dfae22f47..cd2b70bc32a42 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -322,7 +322,7 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Built-in Copilot analysis - Built-in Copilot analysis + Analyse Copilot intégrée @@ -657,7 +657,7 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Dismiss - Dismiss + Ignorer @@ -1155,6 +1155,16 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai {0} doit retourner un flux qui prend en charge les opérations de lecture et de recherche. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Fichiers divers @@ -1385,6 +1395,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Le quantificateur {x,y} ne suit rien This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group référence à un groupe indéfini @@ -2053,7 +2068,7 @@ Vous pouvez concaténer au moins deux plages de caractères. Par exemple, pour s The regular expression construct \P{ name } matches any character that does not belong to a Unicode general category or named block, where name is the category abbreviation or named block name. - La construction d'expression régulière \P{ nom } correspond aux caractères qui n'appartiennent pas à une catégorie générale Unicode ou à un bloc nommé, nom étant l'abréviation de la catégorie ou le nom du bloc nommé. + La construction d'expression régulière \P{ name } correspond aux caractères qui n'appartiennent pas à une catégorie générale Unicode ou à un bloc nommé, nom étant l'abréviation de la catégorie ou le nom du bloc nommé. @@ -2345,7 +2360,7 @@ Il existe une ambiguïté entre les codes d'échappement octaux (par exemple \16 The regular expression construct \p{ name } matches any character that belongs to a Unicode general category or named block, where name is the category abbreviation or named block name. - La construction d'expression régulière \p{ nom } correspond aux caractères qui appartiennent à une catégorie générale Unicode ou à un bloc nommé, nom étant l'abréviation de la catégorie ou le nom du bloc nommé. + La construction d'expression régulière \p{ name } correspond aux caractères qui appartiennent à une catégorie générale Unicode ou à un bloc nommé, nom étant l'abréviation de la catégorie ou le nom du bloc nommé. @@ -2620,6 +2635,26 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Sélection non contenue dans un type. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Silencieux @@ -2680,6 +2715,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Symbole trouvé dans le chemin d’accès de l’assembly '{0}' + + Symbols + Symbols + + Syntax error Erreur de syntaxe @@ -2710,6 +2750,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. La sélection contient un appel de fonction locale sans sa déclaration. @@ -2765,6 +2810,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Impossible de lire le fichier source '{0}' ou le PDB généré pour le projet conteneur. Les changements apportés à ce fichier durant le débogage ne seront pas appliqués tant que son contenu ne correspondra pas à la source générée. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Propriété inconnue diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index d3865a828ff24..4bb62acc56dbf 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -322,7 +322,7 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Built-in Copilot analysis - Built-in Copilot analysis + Analisi Copilot predefinita @@ -657,7 +657,7 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Dismiss - Dismiss + Chiudi @@ -1155,6 +1155,16 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa {0} deve restituire un flusso che supporta operazioni di lettura e ricerca. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files File esterni @@ -1385,6 +1395,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Il quantificatore {x,y} non segue alcun elemento This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group riferimento a gruppo indefinito @@ -2620,6 +2635,26 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' La selezione non è contenuta all'interno di un tipo. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Invisibile @@ -2680,6 +2715,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Simbolo trovato nel percorso assembly '{0}' + + Symbols + Symbols + + Syntax error Errore di sintassi @@ -2710,6 +2750,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. La selezione contiene una chiamata di funzione locale senza la relativa dichiarazione. @@ -2765,6 +2810,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Non è possibile leggere il file di origine '{0}' o il file PDB compilato per il progetto contenitore. Tutte le modifiche apportate a questo file durante il debug non verranno applicate finché il relativo contenuto non corrisponde al codice sorgente compilato. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Proprietà sconosciuta diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 5f9b178ff8c9c..614bf87b47d2b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -322,7 +322,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Built-in Copilot analysis - Built-in Copilot analysis + 組み込みの Copilot の分析 @@ -657,7 +657,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Dismiss - Dismiss + 無視 @@ -1155,6 +1155,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0} は、読み取りとシーク操作をサポートするストリームを返す必要があります。 + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files その他のファイル @@ -1385,6 +1395,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 量指定子 {x,y} の前に何もありません This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group 未定義のグループへの参照 @@ -2620,6 +2635,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 選択が型の内部に含まれていません。 + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent サイレント @@ -2680,6 +2715,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of アセンブリ パス '{0}' にシンボルが見つかりました + + Symbols + Symbols + + Syntax error 構文エラー @@ -2710,6 +2750,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. 選択範囲に、宣言のないローカル関数呼び出しが含まれています。 @@ -2765,6 +2810,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of ソース ファイル '{0}'、またはそれを含むプロジェクト用にビルドされた PDB を読み取れません。デバッグ中にこのファイルに加えられた変更は、そのコンテンツがビルドされたソースと一致するまで適用されません。 + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property 不明なプロパティ diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 7412d68328430..375f752f611ab 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -322,7 +322,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Built-in Copilot analysis - Built-in Copilot analysis + 기본 제공 Copilot 분석 @@ -657,7 +657,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Dismiss - Dismiss + 해제 @@ -1155,6 +1155,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0}은(는) 읽기 및 검색 작업을 지원하는 스트림을 반환해야 합니다. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files 기타 파일 @@ -1385,6 +1395,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 수량자 {x,y} 앞에 아무 것도 없습니다. This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group 정의되지 않은 그룹에 대한 참조 @@ -2620,6 +2635,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 선택 항목이 형식 내에 포함되어 있지 않습니다. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent 침묵 @@ -2680,6 +2715,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 어셈블리 경로 '{0}'에서 기호를 찾음 + + Symbols + Symbols + + Syntax error 구문 오류 @@ -2710,6 +2750,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. 선언이 없는 로컬 함수 호출이 선택 영역에 있습니다. @@ -2765,6 +2810,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 소스 파일 '{0}' 또는 포함하는 프로젝트에 대해 빌드된 PDB를 읽을 수 없습니다. 디버그하는 동안 이 파일의 변경된 모든 내용은 해당 콘텐츠가 빌드된 소스와 일치할 때까지 적용되지 않습니다. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property 알 수 없는 속성 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 062a32c6cf5d7..b0ef2b9c0c2c7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -322,7 +322,7 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Built-in Copilot analysis - Built-in Copilot analysis + Wbudowana analiza funkcji Copilot @@ -657,7 +657,7 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Dismiss - Dismiss + Odrzuć @@ -1155,6 +1155,16 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k {0} musi zwrócić strumień, który obsługuje operacje odczytu i wyszukiwania. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Różne pliki @@ -1385,6 +1395,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Nic nie występuje przed kwantyfikatorem {x,y} This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group odwołanie do niezdefiniowanej grupy @@ -2053,7 +2068,7 @@ Można połączyć dwa lub więcej zakresów znaków. Na przykład aby określi The regular expression construct \P{ name } matches any character that does not belong to a Unicode general category or named block, where name is the category abbreviation or named block name. - Konstrukcja wyrażenia regularnego \P{ nazwa } pasuje do dowolnego znaku, który nie należy do kategorii ogólnej znaków Unicode ani do bloku nazwanego, gdzie „nazwa” to skrót kategorii lub nazwa bloku nazwanego. + Konstrukcja wyrażenia regularnego \P{ name } pasuje do dowolnego znaku, który nie należy do kategorii ogólnej znaków Unicode ani do bloku nazwanego, gdzie „nazwa” to skrót kategorii lub nazwa bloku nazwanego. @@ -2345,7 +2360,7 @@ Istnieje niejednoznaczność między ósemkowymi kodami ucieczki (takimi jak \16 The regular expression construct \p{ name } matches any character that belongs to a Unicode general category or named block, where name is the category abbreviation or named block name. - Konstrukcja wyrażenia regularnego \p{ nazwa } pasuje do dowolnego znaku należącego do ogólnej kategorii Unicode lub bloku nazwanego, gdzie „nazwa” to skrót kategorii lub nazwa bloku nazwanego. + Konstrukcja wyrażenia regularnego \p{ name } pasuje do dowolnego znaku należącego do ogólnej kategorii Unicode lub bloku nazwanego, gdzie „nazwa” to skrót kategorii lub nazwa bloku nazwanego. @@ -2620,6 +2635,26 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Zaznaczenie nie jest uwzględnione w typie. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Cichy @@ -2680,6 +2715,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Znaleziono symbol w ścieżce zestawu „{0}” + + Symbols + Symbols + + Syntax error Błąd składniowy @@ -2710,6 +2750,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. Zaznaczenie zawiera wywołanie funkcji lokalnej bez jego deklaracji. @@ -2765,6 +2810,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Nie można odczytać pliku źródłowego „{0}” lub pliku PDB skompilowanego dla projektu, w którym jest on zawarty. Wszystkie zmiany wprowadzone w tym pliku podczas debugowania nie zostaną zastosowane do czasu, aż jego zawartość będzie zgodna ze skompilowanym źródłem. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Nieznana właściwość diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 8ad5b710974a7..2a0f5278d86fd 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -322,7 +322,7 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Built-in Copilot analysis - Built-in Copilot analysis + Análise interna do Copilot @@ -372,7 +372,7 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess TODO - TODO + TODO "TODO" is an indication that there is work still to be done. @@ -657,7 +657,7 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Dismiss - Dismiss + Ignorar @@ -1155,6 +1155,16 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess {0} precisa retornar um fluxo compatível com as operações de leitura e busca. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Arquivos Diversos @@ -1385,6 +1395,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Nada precede o quantificador {x,y} This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group referência a grupo indefinido @@ -2620,6 +2635,26 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Seleção não contida dentro de um tipo. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Silencioso @@ -2680,6 +2715,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Símbolo encontrado no caminho de montagem '{0}' + + Symbols + Symbols + + Syntax error Erro de sintaxe @@ -2687,12 +2727,12 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas TODO: free unmanaged resources (unmanaged objects) and override finalizer - TODO: free unmanaged resources (unmanaged objects) and override finalizer + TODO: liberar recursos não gerenciados (objetos não gerenciados) e substituir o finalizador TODO: override finalizer only if '{0}' has code to free unmanaged resources - TODO: override finalizer only if '{0}' has code to free unmanaged resources + TODO: substituir o finalizador somente se '{0}' tiver o código para liberar recursos não gerenciados @@ -2710,6 +2750,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. A seleção contém uma chamada de função local sem sua declaração. @@ -2765,6 +2810,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Não é possível ler o arquivo de origem '{0}' ou o PDB criado para o projeto que o contém. Todas as alterações feitas neste arquivo durante a depuração não serão aplicadas até que o conteúdo corresponda ao código-fonte compilado. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Propriedade desconhecida @@ -4237,12 +4287,12 @@ Deseja continuar? TODO: dispose managed state (managed objects) - TODO: dispose managed state (managed objects) + TODO: descartar o estado gerenciado (objetos gerenciados) TODO: set large fields to null - TODO: set large fields to null + TODO: definir campos grandes como nulos diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 5b4ac56bd6adb..5a5d439d0e939 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -322,7 +322,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Built-in Copilot analysis - Built-in Copilot analysis + Встроенный анализ Copilot @@ -372,7 +372,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma TODO - TODO + TODO "TODO" is an indication that there is work still to be done. @@ -657,7 +657,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Dismiss - Dismiss + Отклонить @@ -1155,6 +1155,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0} должен возвращать поток, который поддерживает операции чтения и поиска. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Прочие файлы @@ -1385,6 +1395,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Отсутствуют элементы перед квантификатором {x,y} This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group Ссылка на неопределенную группу @@ -2053,7 +2068,7 @@ Two or more character ranges can be concatenated. For example, to specify the ra The regular expression construct \P{ name } matches any character that does not belong to a Unicode general category or named block, where name is the category abbreviation or named block name. - Конструкция регулярного выражения \P{ имя } соответствует любому символу, который не относится к общей категории Юникода или именованному блоку, где "имя" — это сокращение названия категории или имя именованного блока. + Конструкция регулярного выражения \P{ name } соответствует любому символу, который не относится к общей категории Юникода или именованному блоку, где "имя" — это сокращение названия категории или имя именованного блока. @@ -2345,7 +2360,7 @@ There is an ambiguity between octal escape codes (such as \16) and \number backr The regular expression construct \p{ name } matches any character that belongs to a Unicode general category or named block, where name is the category abbreviation or named block name. - Конструкция регулярного выражения \p{ имя } соответствует любому символу, который относится к общей категории Юникода или именованному блоку, где "имя" — это сокращение названия категории или имя именованного блока. + Конструкция регулярного выражения \p{ name } соответствует любому символу, который относится к общей категории Юникода или именованному блоку, где "имя" — это сокращение названия категории или имя именованного блока. @@ -2620,6 +2635,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Выделенный фрагмент не входит в тип. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Тихий @@ -2680,6 +2715,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Найден символ в пути сборки "{0}" + + Symbols + Symbols + + Syntax error Синтаксическая ошибка @@ -2710,6 +2750,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. Выделенный фрагмент содержит вызов локальной функции без ее объявления. @@ -2765,6 +2810,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Не удалось считать исходный файл "{0}" или PDB, созданный для содержащего проекта. Все изменения, внесенные в этот файл во время отладки, не будут применены, пока содержимое файла не будет соответствовать созданному источнику. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Неизвестное свойство diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 967adb2829322..c6ed18ace5570 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -322,7 +322,7 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Built-in Copilot analysis - Built-in Copilot analysis + Yerleşik Copilot analizi @@ -657,7 +657,7 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Dismiss - Dismiss + Kapat @@ -1155,6 +1155,16 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be {0} okuma ve arama işlemlerini destekleyen bir akış döndürmelidir. + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files Diğer Dosyalar @@ -1385,6 +1395,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Niceleyici {x,y} hiçbir şeyi takip etmiyor This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group tanımsız grubuna başvuru @@ -2620,6 +2635,26 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri Seçim bir türün içinde yer almıyor. + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent Sessiz @@ -2680,6 +2715,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri “{0}” derleme yolunda sembol bulundu + + Symbols + Symbols + + Syntax error Söz dizimi hatası @@ -2710,6 +2750,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. Seçim, bildirimi olmadan bir yerel işlev çağrısı içeriyor. @@ -2765,6 +2810,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri '{0}' kaynak dosyası veya içeren proje için oluşturulan PDB okunamıyor. Hata ayıklama sırasında bu dosyada yapılan değişiklikler, dosyanın içeriği oluşturulan kaynakla eşleşene kadar uygulanmaz. + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property Bilinmeyen Özellik diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 0a8cc24393276..9853290a52d4e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -322,7 +322,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Built-in Copilot analysis - Built-in Copilot analysis + 内置 Copilot 分析 @@ -657,7 +657,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Dismiss - Dismiss + 消除 @@ -1155,6 +1155,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0} 必须返回支持读取和查找操作的流。 + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files 杂项文件 @@ -1385,6 +1395,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 限定符 {x,y} 前没有任何内容 This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group 对未定义组的引用 @@ -2053,7 +2068,7 @@ Two or more character ranges can be concatenated. For example, to specify the ra The regular expression construct \P{ name } matches any character that does not belong to a Unicode general category or named block, where name is the category abbreviation or named block name. - 正则表达式构造 \P{name} 匹配不属于 Unicode 通用类别或命名块的任何字符,其中 "name" 是类别缩写或命名块名称。 + 正则表达式构造 \P{ name } 匹配不属于 Unicode 通用类别或命名块的任何字符,其中 "name" 是类别缩写或命名块名称。 @@ -2345,7 +2360,7 @@ There is an ambiguity between octal escape codes (such as \16) and \number backr The regular expression construct \p{ name } matches any character that belongs to a Unicode general category or named block, where name is the category abbreviation or named block name. - 正则表达式构造 \p{name} 匹配属于 Unicode 通用类别或命名块的任何字符,其中 "name" 是类别缩写或命名块名称。 + 正则表达式构造 \p{ name } 匹配属于 Unicode 通用类别或命名块的任何字符,其中 "name" 是类别缩写或命名块名称。 @@ -2620,6 +2635,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 所选内容不包含在类型内。 + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent 无提示 @@ -2680,6 +2715,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 在程序集路径 "{0}" 中找到符号 + + Symbols + Symbols + + Syntax error 语法错误 @@ -2710,6 +2750,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. 所选内容包含不带声明的本地函数调用。 @@ -2765,6 +2810,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 无法读取源文件 "{0}" 或为包含项目生成的 PDB。在调试期间对此文件所做的任何更改都不会应用,直到其内容与生成的源匹配为止。 + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property 未知属性 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index f5da4b37f6d34..824278aac6021 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -322,7 +322,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Built-in Copilot analysis - Built-in Copilot analysis + 內建 Copilot 分析 @@ -657,7 +657,7 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Dismiss - Dismiss + 關閉 @@ -1155,6 +1155,16 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0} 必須傳回支援讀取和搜尋作業的資料流。 + + Method '{0}' must be static and non-generic + Method '{0}' must be static and non-generic + + + + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + Method '{0}' must have a single parameter of type '{1}' and return '{2}' + + Miscellaneous Files 其他檔案 @@ -1385,6 +1395,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 數量詞 {x,y} 前面沒有任何項目 This is an error message shown to the user when they write an invalid Regular Expression. Example: * + + Query + Query + + reference to undefined group 對未定義群組的參考 @@ -2620,6 +2635,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 選取範圍未包含在類型內。 + + Semantic Search + Semantic Search + + + + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + Semantic search is only supported when code analysis runs in a separate process on the latest .NET (see Tools > Options > Text Editor > C# > Advanced). + + + + Semantic search query failed to compile + Semantic search query failed to compile + + + + Semantic search query terminated with exception + Semantic search query terminated with exception + + Silent 靜音 @@ -2680,6 +2715,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 在組件路徑 '{0}' 中找到符號 + + Symbols + Symbols + + Syntax error 語法錯誤 @@ -2710,6 +2750,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 + + The query does not specify '{0}' method or top-level function + The query does not specify '{0}' method or top-level function + + The selection contains a local function call without its declaration. 選取範圍包含區域函式呼叫,但不含其宣告。 @@ -2765,6 +2810,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 無法讀取來源檔案 '{0}' 或為包含該檔案之專案所建置的 PDB。等到此檔案的內容與已建置的來源一致後,才會套用於偵錯期間對此檔案所做的所有變更。 + + Unable to load type '{0}': '{1}' + Unable to load type '{0}': '{1}' + + Unknown property 未知屬性 diff --git a/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs b/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs index 30b0185c73e23..b0bdf4fd1c942 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActionsLegacy/AbstractCodeActionOrUserDiagnosticTest_NoEditor.cs @@ -22,13 +22,11 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; @@ -156,9 +154,7 @@ private protected virtual IDocumentServiceProvider GetDocumentServiceProvider() protected virtual TestComposition GetComposition() => FeaturesTestCompositions.Features - .AddExcludedPartTypes(typeof(IDiagnosticUpdateSourceRegistrationService)) - .AddParts(typeof(MockDiagnosticUpdateSourceRegistrationService)) - .AddAssemblies(typeof(DiagnosticService).Assembly); + .AddAssemblies(typeof(DiagnosticIncrementalAnalyzer).Assembly); protected virtual void InitializeWorkspace(TestWorkspace workspace, TestParameters parameters) { diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs index aa3787f934d57..8ad3f78c6dac2 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.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.Collections.Immutable; using System.ComponentModel.Composition; +using Microsoft.CodeAnalysis.BrokeredServices; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Composition; @@ -30,13 +32,16 @@ internal class ServiceBrokerFactory private readonly ExportProvider _exportProvider; private Task _bridgeCompletionTask; private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly ImmutableArray _onServiceBrokerInitialized; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public ServiceBrokerFactory(ExportProvider exportProvider) + public ServiceBrokerFactory([ImportMany] IEnumerable onServiceBrokerInitialized, + ExportProvider exportProvider) { _exportProvider = exportProvider; _bridgeCompletionTask = Task.CompletedTask; + _onServiceBrokerInitialized = onServiceBrokerInitialized.ToImmutableArray(); } /// @@ -64,6 +69,17 @@ public async Task CreateAsync() Contract.ThrowIfFalse(_container == null, "We should only create one container."); _container = await BrokeredServiceContainer.CreateAsync(_exportProvider, _cancellationTokenSource.Token); + + foreach (var onInitialized in _onServiceBrokerInitialized) + { + try + { + onInitialized.OnServiceBrokerInitialized(_container.GetFullAccessServiceBroker()); + } + catch (Exception) + { + } + } } public async Task CreateAndConnectAsync(string brokeredServicePipeName) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs index bd87c4f2b5155..dd0618831e79c 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/Descriptors.cs @@ -43,6 +43,9 @@ internal class Descriptors { RemoteProjectInitializationStatusService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, { BrokeredServiceDescriptors.SolutionSnapshotProvider.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, { BrokeredServiceDescriptors.DebuggerManagedHotReloadService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, + { BrokeredServiceDescriptors.HotReloadSessionNotificationService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, + { BrokeredServiceDescriptors.ManagedHotReloadAgentManagerService.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, + { BrokeredServiceDescriptors.MauiLaunchCustomizerServiceDescriptor.Moniker, new ServiceRegistration(ServiceAudience.Local, null, allowGuestClients: false) }, }.ToImmutableDictionary(); public static ServiceJsonRpcDescriptor CreateDescriptor(ServiceMoniker serviceMoniker) => new( diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs index a6e3d506bf119..404e302782684 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspFileChangeWatcher.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.ProjectSystem; @@ -64,7 +65,8 @@ private class FileChangeContext : IFileChangeContext /// 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(StringComparer.Ordinal); + private readonly Dictionary _watchedFiles = new Dictionary(_stringComparer); + private static readonly StringComparer _stringComparer = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; public FileChangeContext(ImmutableArray watchedDirectories, LspFileChangeWatcher lspFileChangeWatcher) { diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs index 21d0dce581995..1d27e3e898fa3 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs @@ -44,7 +44,7 @@ public LanguageServerWorkspaceFactory( var razorSourceGenerator = serverConfigurationFactory?.ServerConfiguration?.RazorSourceGenerator; ProjectSystemHostInfo = new ProjectSystemHostInfo( DynamicFileInfoProviders: dynamicFileInfoProviders.ToImmutableArray(), - new ProjectSystemDiagnosticSource(), + ProjectSystemDiagnosticSource.Instance, new HostDiagnosticAnalyzerProvider(razorSourceGenerator)); TargetFrameworkManager = projectTargetFrameworkManager; diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectSystemDiagnosticSource.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectSystemDiagnosticSource.cs index 57a11e0431565..5fc406f39698a 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectSystemDiagnosticSource.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectSystemDiagnosticSource.cs @@ -6,17 +6,12 @@ using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; -internal class ProjectSystemDiagnosticSource : IProjectSystemDiagnosticSource -{ - public void ClearAllDiagnosticsForProject(ProjectId projectId) - { - } - public void ClearAnalyzerReferenceDiagnostics(AnalyzerFileReference fileReference, string language, ProjectId projectId) - { - } +internal sealed class ProjectSystemDiagnosticSource : IProjectSystemDiagnosticSource +{ + public static readonly ProjectSystemDiagnosticSource Instance = new(); - public void ClearDiagnosticsForProject(ProjectId projectId, object key) + private ProjectSystemDiagnosticSource() { } @@ -24,9 +19,4 @@ public DiagnosticData CreateAnalyzerLoadFailureDiagnostic(AnalyzerLoadFailureEve { return DocumentAnalysisExecutor.CreateAnalyzerLoadFailureDiagnostic(e, fullPath, projectId, language); } - - public void UpdateDiagnosticsForProject(ProjectId projectId, object key, IEnumerable items) - { - // TODO: actually store the diagnostics - } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs index 81ad2a04ee55d..d62627d2742e4 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/VSCodeAnalyzerLoader.cs @@ -2,14 +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.Immutable; using System.Composition; using System.Reflection; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Services; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.Extensions.Logging; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; @@ -17,20 +13,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; [Export(typeof(VSCodeAnalyzerLoader)), Shared] internal class VSCodeAnalyzerLoader { - private readonly IDiagnosticAnalyzerService _analyzerService; - private readonly DiagnosticService _diagnosticService; - [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VSCodeAnalyzerLoader(IDiagnosticAnalyzerService analyzerService, IDiagnosticService diagnosticService) + public VSCodeAnalyzerLoader() { - _analyzerService = analyzerService; - _diagnosticService = (DiagnosticService)diagnosticService; } +#pragma warning disable CA1822 // Mark members as static public void InitializeDiagnosticsServices() +#pragma warning restore CA1822 // Mark members as static { - _diagnosticService.Register((IDiagnosticUpdateSource)_analyzerService); } public static IAnalyzerAssemblyLoader CreateAnalyzerAssemblyLoader(ExtensionAssemblyManager extensionAssemblyManager, ILogger logger) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj index b628f743b2ed0..fa42a2d113562 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj @@ -71,6 +71,7 @@ + diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.cs.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.cs.xlf index fce49dc95612a..ca6673bf876c9 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.cs.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.cs.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Vyvolaná výjimka: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Čtení souboru .runsettings na {0} se nezdařilo:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.de.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.de.xlf index 606734d5d3b4f..61878d5f160a9 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.de.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.de.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Ausgelöste Ausnahme: „{0}“ @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Fehler beim Lesen der RUNSETTINGS-Datei unter {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.es.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.es.xlf index 130e824fefda5..ffbbb2b8bca28 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.es.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.es.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Se produjo una excepción: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + No se pudo leer el archivo .runsettings en {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.fr.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.fr.xlf index 79ac14bee0805..6941c95ef6fb4 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.fr.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.fr.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Exception levée : « {0} » @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Échec de la lecture du fichier .runsettings à l'adresse {0} :{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.it.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.it.xlf index e28f18705f9cd..27e970862bbae 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.it.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.it.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + È stata generata un'eccezione: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Non è stato possibile leggere il file con estensione runsettings in {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ja.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ja.xlf index 18ea96ef8d519..c4940eb7c664d 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ja.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ja.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + 例外がスローされました: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + {0}:{1} の .runsettings ファイルを読み取れできませんでした diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ko.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ko.xlf index 546715d7d11a6..56ecb0074946d 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ko.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ko.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + 예외 throw됨: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + {0}:{1}에서 .runsettings 파일을 읽지 못했습니다. diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pl.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pl.xlf index 93450a5eb8081..6bb6fb61ba444 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pl.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pl.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Zgłoszony wyjątek: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Nie można odczytać pliku .runsettings w {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pt-BR.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pt-BR.xlf index 810426026e0fa..0be751a2cb52d 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pt-BR.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.pt-BR.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Exceção gerada: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Falha ao ler o arquivo .runsettings em {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ru.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ru.xlf index ffb1d27e0ec3b..9f6ffe157bbd1 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ru.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.ru.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Возникло исключение: "{0}" @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Не удалось прочитать файл RUNSETTINGS в {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.tr.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.tr.xlf index c96b2c883235f..a57b21bf3164d 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.tr.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.tr.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + Özel durum oluştu: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + Şu konumdaki .runsettings dosyası okunamadı: {0}:{1} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hans.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hans.xlf index 4ae377a65b07b..9c0e452b77054 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hans.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hans.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + 引发了异常: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + 无法读取 {0}:{1}的 .runsettings 文件 diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hant.xlf b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hant.xlf index 88046acb63b22..b3ce5e2fdaaec 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hant.xlf +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/xlf/LanguageServerResources.zh-Hant.xlf @@ -44,7 +44,7 @@ Exception thrown: {0} - Exception thrown: {0} + 擲回的例外狀況: {0} @@ -59,7 +59,7 @@ Failed to read .runsettings file at {0}:{1} - Failed to read .runsettings file at {0}:{1} + 無法讀取位於 {0} 的 .runsettings 檔案: {1} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Binary/Microsoft.CommonLanguageServerProtocol.Framework.Binary.csproj b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Binary/Microsoft.CommonLanguageServerProtocol.Framework.Binary.csproj index 482f6dfbdeba7..b82d6a2a340bd 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Binary/Microsoft.CommonLanguageServerProtocol.Framework.Binary.csproj +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Binary/Microsoft.CommonLanguageServerProtocol.Framework.Binary.csproj @@ -12,8 +12,13 @@ netstandard2.0 Microsoft.CommonLanguageServerProtocol.Framework - - false + + true + Microsoft.CommonLanguageServerProtocol.Framework.Binary + + A legacy binary implementation of Microsoft.CommonLanguageServerProtocol.Framework. + + BINARY_COMPAT diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 3a9f045e2e595..c6a06c30f422c 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -95,6 +95,17 @@ internal static partial class ProtocolConversions { WellKnownTags.NuGet, ImmutableArray.Create(LSP.CompletionItemKind.Text) } }.ToImmutableDictionary(); + /// + /// Mapping from tags to LSP completion item tags. The value lists the potential LSP tags from + /// least-preferred to most preferred. More preferred kinds will be chosen if the client states they support + /// it. This mapping allows values including extensions to the kinds defined by VS (but not in the core LSP + /// spec). + /// + public static readonly ImmutableDictionary> RoslynTagToCompletionItemTags = new Dictionary>() + { + { WellKnownTags.Deprecated, ImmutableArray.Create(LSP.CompletionItemTag.Deprecated) }, + }.ToImmutableDictionary(); + // TO-DO: More LSP.CompletionTriggerKind mappings are required to properly map to Roslyn CompletionTriggerKinds. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1178726 public static async Task LSPToRoslynCompletionTriggerAsync( diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index 021e607a10c04..f06bae504b101 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -23,7 +23,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -255,20 +254,10 @@ private static async Task> GetCopilotDiagnosticsA CodeActionRequestPriority? priority, CancellationToken cancellationToken) { - if (!(priority is null or CodeActionRequestPriority.Low) - || document is not Document sourceDocument) - { - return []; - } - - // Expand the fixable range for Copilot diagnostics to containing method. - // TODO: Share the below code with other places we compute containing method for Copilot analysis. - var root = await sourceDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var syntaxFacts = sourceDocument.GetRequiredLanguageService(); - var containingMethod = syntaxFacts.GetContainingMethodDeclaration(root, range.Start, useFullSpan: false); - range = containingMethod?.Span ?? range; + if (priority is null or CodeActionRequestPriority.Low) + return await document.GetCachedCopilotDiagnosticsAsync(range, cancellationToken).ConfigureAwait(false); - return await document.GetCachedCopilotDiagnosticsAsync(range, cancellationToken).ConfigureAwait(false); + return []; } private static SortedDictionary> ConvertToMap( diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 507922c291017..aac07fce0e0f2 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -26,12 +26,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics [Shared] internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService { - private const string DiagnosticsUpdatedEventName = "DiagnosticsUpdated"; - - // use eventMap and taskQueue to serialize events - private readonly EventMap _eventMap = new(); - private readonly TaskQueue _eventQueue; - public DiagnosticAnalyzerInfoCache AnalyzerInfoCache { get; private set; } public IAsynchronousOperationListener Listener { get; } @@ -39,11 +33,11 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService private readonly ConditionalWeakTable _map = new(); private readonly ConditionalWeakTable.CreateValueCallback _createIncrementalAnalyzer; + private readonly IDiagnosticsRefresher _diagnosticsRefresher; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public DiagnosticAnalyzerService( - IDiagnosticUpdateSourceRegistrationService registrationService, IAsynchronousOperationListenerProvider listenerProvider, DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache, IGlobalOptionService globalOptions, @@ -52,18 +46,14 @@ public DiagnosticAnalyzerService( AnalyzerInfoCache = globalCache.AnalyzerInfoCache; Listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService); GlobalOptions = globalOptions; - + _diagnosticsRefresher = diagnosticsRefresher; _createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback; - _eventQueue = new TaskQueue(Listener, TaskScheduler.Default); - - registrationService.Register(this); - globalOptions.AddOptionChangedHandler(this, (_, e) => { if (IsGlobalOptionAffectingDiagnostics(e.Option)) { - diagnosticsRefresher.RequestWorkspaceRefresh(); + RequestDiagnosticRefresh(); } }); } @@ -75,9 +65,8 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) option == SolutionCrawlerOptionsStorage.SolutionBackgroundAnalysisScopeOption || option == SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption; - public void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) - { - } + public void RequestDiagnosticRefresh() + => _diagnosticsRefresher?.RequestWorkspaceRefresh(); public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( TextDocument document, diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.cs deleted file mode 100644 index 62a6da55c689b..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_BuildSynchronization.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. - -#nullable disable - -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - internal partial class DiagnosticAnalyzerService - { - /// - /// Synchronize build errors with live error. - /// - public ValueTask SynchronizeWithBuildAsync( - Workspace workspace, - ImmutableDictionary> diagnostics, - TaskQueue postBuildAndErrorListRefreshTaskQueue, - bool onBuildCompleted, - CancellationToken cancellationToken) - { - return _map.TryGetValue(workspace, out var analyzer) - ? analyzer.SynchronizeWithBuildAsync(diagnostics, postBuildAndErrorListRefreshTaskQueue, onBuildCompleted, cancellationToken) - : default; - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index b52fa731ff259..99d9760bc40ca 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -5,8 +5,6 @@ using System; using Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; @@ -23,9 +21,9 @@ private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspac // subscribe to active context changed event for new workspace workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - return new DiagnosticIncrementalAnalyzer(this, CorrelationIdFactory.GetNextId(), workspace, AnalyzerInfoCache); + return new DiagnosticIncrementalAnalyzer(this, workspace, AnalyzerInfoCache); } private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) - => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); + => RequestDiagnosticRefresh(); } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs deleted file mode 100644 index 2d4a1d39db757..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs +++ /dev/null @@ -1,103 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - internal partial class DiagnosticAnalyzerService : IDiagnosticUpdateSource - { - public event EventHandler> DiagnosticsUpdated - { - add - { - _eventMap.AddEventHandler(DiagnosticsUpdatedEventName, value); - } - - remove - { - _eventMap.RemoveEventHandler(DiagnosticsUpdatedEventName, value); - } - } - - public event EventHandler DiagnosticsCleared - { - add - { - // don't do anything. this update source doesn't use cleared event - } - - remove - { - // don't do anything. this update source doesn't use cleared event - } - } - - internal void RaiseDiagnosticsUpdated(ImmutableArray args) - { - if (args.IsEmpty) - return; - - // all diagnostics events are serialized. - var ev = _eventMap.GetEventHandlers>>(DiagnosticsUpdatedEventName); - if (ev.HasHandlers) - { - _eventQueue.ScheduleTask(nameof(RaiseDiagnosticsUpdated), () => ev.RaiseEvent(static (handler, arg) => handler(arg.self, arg.args), (self: this, args)), CancellationToken.None); - } - } - - internal void RaiseBulkDiagnosticsUpdated(Action>> eventAction) - { - // all diagnostics events are serialized. - var ev = _eventMap.GetEventHandlers>>(DiagnosticsUpdatedEventName); - if (ev.HasHandlers) - { - // we do this bulk update to reduce number of tasks (with captured data) enqueued. - // we saw some "out of memory" due to us having long list of pending tasks in memory. - // this is to reduce for such case to happen. - void raiseEvents(ImmutableArray args) - { - if (args.IsEmpty) - return; - - ev.RaiseEvent( - static (handler, arg) => handler(arg.self, arg.args), - (self: this, args)); - } - - _eventQueue.ScheduleTask(nameof(RaiseDiagnosticsUpdated), () => eventAction(raiseEvents), CancellationToken.None); - } - } - - internal void RaiseBulkDiagnosticsUpdated(Func>, Task> eventActionAsync) - { - // all diagnostics events are serialized. - var ev = _eventMap.GetEventHandlers>>(DiagnosticsUpdatedEventName); - if (ev.HasHandlers) - { - // we do this bulk update to reduce number of tasks (with captured data) enqueued. - // we saw some "out of memory" due to us having long list of pending tasks in memory. - // this is to reduce for such case to happen. - void raiseEvents(ImmutableArray args) - { - ev.RaiseEvent( - static (handler, arg) => - { - if (!arg.args.IsEmpty) - handler(arg.self, arg.args); - }, - (self: this, args)); - } - - _eventQueue.ScheduleTask(nameof(RaiseDiagnosticsUpdated), () => eventActionAsync(raiseEvents), CancellationToken.None); - } - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs deleted file mode 100644 index 38e15e438c93e..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService.cs +++ /dev/null @@ -1,269 +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.Composition; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Common; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - [Export(typeof(IDiagnosticService)), Shared] - internal partial class DiagnosticService : IDiagnosticService, IDisposable - { - private const string DiagnosticsUpdatedEventName = "DiagnosticsUpdated"; - - private readonly EventMap _eventMap = new(); - private readonly CancellationTokenSource _eventQueueCancellation = new(); - private readonly AsyncBatchingWorkQueue<(BatchOperation operation, IDiagnosticUpdateSource source, ImmutableArray argsCollection)> _eventQueue; - - private readonly object _gate = new(); - private readonly Dictionary>> _map = []; - - private ImmutableHashSet _updateSources; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DiagnosticService( - IAsynchronousOperationListenerProvider listenerProvider, - [ImportMany] IEnumerable> eventListeners) - { - // we use registry service rather than doing MEF import since MEF import method can have race issue where - // update source gets created before aggregator - diagnostic service - is created and we will lose events - // fired before the aggregator is created. - _updateSources = []; - - // queue to serialize events. - _eventQueue = new AsyncBatchingWorkQueue<(BatchOperation operation, IDiagnosticUpdateSource source, ImmutableArray argsCollection)>( - delay: TimeSpan.Zero, - ProcessEventsBatchAsync, - listenerProvider.GetListener(FeatureAttribute.DiagnosticService), - _eventQueueCancellation.Token); - } - - private enum BatchOperation - { - DiagnosticsUpdated, - DiagnosticsCleared, - } - - public event EventHandler> DiagnosticsUpdated - { - add - { - _eventMap.AddEventHandler(DiagnosticsUpdatedEventName, value); - } - - remove - { - _eventMap.RemoveEventHandler(DiagnosticsUpdatedEventName, value); - } - } - - void IDisposable.Dispose() - { - _eventQueueCancellation.Cancel(); - } - - private ValueTask ProcessEventsBatchAsync(ImmutableSegmentedList<(BatchOperation operation, IDiagnosticUpdateSource source, ImmutableArray argsCollection)> batch, CancellationToken cancellationToken) - { - var ev = _eventMap.GetEventHandlers>>(DiagnosticsUpdatedEventName); - - foreach (var (operation, source, argsCollection) in batch) - { - if (operation == BatchOperation.DiagnosticsUpdated) - { - var updatedArgsCollection = UpdateDataMap(source, argsCollection); - if (updatedArgsCollection.IsEmpty) - { - // there is no change, nothing to raise events for. - continue; - } - - ev.RaiseEvent(static (handler, arg) => handler(arg.source, arg.updatedArgsCollection), (source, updatedArgsCollection)); - } - else if (operation == BatchOperation.DiagnosticsCleared) - { - using var argsBuilder = TemporaryArray.Empty; - - if (!ClearDiagnosticsReportedBySource(source, ref argsBuilder.AsRef())) - { - // there is no change, nothing to raise events for. - continue; - } - - // don't create event listener if it haven't created yet. if there is a diagnostic to remove - // listener should have already created since all events are done in the serialized queue - ev.RaiseEvent(static (handler, arg) => handler(arg.source, arg.args), (source, args: argsBuilder.ToImmutableAndClear())); - } - else - { - throw ExceptionUtilities.UnexpectedValue(operation); - } - } - - return ValueTaskFactory.CompletedTask; - } - - private void RaiseDiagnosticsUpdated(IDiagnosticUpdateSource source, ImmutableArray argsCollection) - { - _eventQueue.AddWork((BatchOperation.DiagnosticsUpdated, source, argsCollection)); - } - - private void RaiseDiagnosticsCleared(IDiagnosticUpdateSource source) - { - _eventQueue.AddWork((BatchOperation.DiagnosticsCleared, source, ImmutableArray.Empty)); - } - - private ImmutableArray UpdateDataMap(IDiagnosticUpdateSource source, ImmutableArray argsCollection) - { - // we expect source who uses this ability to have small number of diagnostics. - lock (_gate) - { - var result = argsCollection.WhereAsArray(args => - { - Debug.Assert(_updateSources.Contains(source)); - - var diagnostics = args.Diagnostics; - - // check cheap early bail out - if (diagnostics.Length == 0 && !_map.ContainsKey(source)) - { - // no new diagnostic, and we don't have update source for it. - return false; - } - - // 2 different workspaces (ex, PreviewWorkspaces) can return same Args.Id, we need to - // distinguish them. so we separate diagnostics per workspace map. - var workspaceMap = _map.GetOrAdd(source, _ => []); - - if (diagnostics.Length == 0 && !workspaceMap.ContainsKey(args.Workspace)) - { - // no new diagnostic, and we don't have workspace for it. - return false; - } - - var diagnosticDataMap = workspaceMap.GetOrAdd(args.Workspace, _ => []); - - diagnosticDataMap.Remove(args.Id); - if (diagnosticDataMap.Count == 0 && diagnostics.Length == 0) - { - workspaceMap.Remove(args.Workspace); - - if (workspaceMap.Count == 0) - { - _map.Remove(source); - } - - return true; - } - - if (diagnostics.Length > 0) - { - var data = new Data(args, diagnostics); - diagnosticDataMap.Add(args.Id, data); - } - - return true; - }); - - return result; - } - } - - private bool ClearDiagnosticsReportedBySource(IDiagnosticUpdateSource source, ref TemporaryArray removed) - { - // we expect source who uses this ability to have small number of diagnostics. - lock (_gate) - { - Debug.Assert(_updateSources.Contains(source)); - - // 2 different workspaces (ex, PreviewWorkspaces) can return same Args.Id, we need to - // distinguish them. so we separate diagnostics per workspace map. - if (!_map.TryGetValue(source, out var workspaceMap)) - { - return false; - } - - foreach (var (workspace, map) in workspaceMap) - { - foreach (var (id, data) in map) - { - removed.Add(DiagnosticsUpdatedArgs.DiagnosticsRemoved(id, data.Workspace, solution: null, data.ProjectId, data.DocumentId)); - } - } - - // all diagnostics from the source is cleared - _map.Remove(source); - return true; - } - } - - private void OnDiagnosticsUpdated(object? sender, ImmutableArray e) - { - AssertIfNull(e.SelectManyAsArray(e => e.Diagnostics)); - - // all events are serialized by async event handler - RaiseDiagnosticsUpdated((IDiagnosticUpdateSource)sender!, e); - } - - private void OnCleared(object? sender, EventArgs e) - { - // all events are serialized by async event handler - RaiseDiagnosticsCleared((IDiagnosticUpdateSource)sender!); - } - - [Conditional("DEBUG")] - private static void AssertIfNull(ImmutableArray diagnostics) - { - for (var i = 0; i < diagnostics.Length; i++) - { - AssertIfNull(diagnostics[i]); - } - } - - [Conditional("DEBUG")] - private static void AssertIfNull(T obj) - where T : class - { - if (obj == null) - { - Debug.Assert(false, "who returns invalid data?"); - } - } - - private readonly struct Data - { - public readonly Workspace Workspace; - public readonly ProjectId? ProjectId; - public readonly DocumentId? DocumentId; - public readonly object Id; - public readonly ImmutableArray Diagnostics; - - public Data(UpdatedEventArgs args) - : this(args, []) - { - } - - public Data(UpdatedEventArgs args, ImmutableArray diagnostics) - { - Workspace = args.Workspace; - ProjectId = args.ProjectId; - DocumentId = args.DocumentId; - Id = args.Id; - Diagnostics = diagnostics; - } - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService_UpdateSourceRegistrationService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService_UpdateSourceRegistrationService.cs deleted file mode 100644 index f586248984e64..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticService_UpdateSourceRegistrationService.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 System.Composition; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - [Export(typeof(IDiagnosticUpdateSourceRegistrationService))] - internal partial class DiagnosticService : IDiagnosticUpdateSourceRegistrationService - { - public void Register(IDiagnosticUpdateSource source) - { - lock (_gate) - { - if (_updateSources.Contains(source)) - { - return; - } - - _updateSources = _updateSources.Add(source); - - source.DiagnosticsUpdated += OnDiagnosticsUpdated; - source.DiagnosticsCleared += OnCleared; - } - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index a2fd206083a6a..05b432309c757 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -12,7 +11,6 @@ using Microsoft.CodeAnalysis.Diagnostics.Telemetry; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -22,159 +20,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - /// - /// Return all cached local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer). - /// Otherwise, return null. - /// For the latter case, indicates if the analyzer is suppressed - /// for the given document/project. If suppressed, the caller does not need to compute the diagnostics for the given - /// analyzer. Otherwise, diagnostics need to be computed. - /// - private (ActiveFileState, DocumentAnalysisData?) TryGetCachedDocumentAnalysisData( - TextDocument document, StateSet stateSet, - AnalysisKind kind, VersionStamp version, - BackgroundAnalysisScope analysisScope, - CompilerDiagnosticsScope compilerDiagnosticsScope, - bool isActiveDocument, bool isVisibleDocument, - bool isOpenDocument, bool isGeneratedRazorDocument, - CancellationToken cancellationToken, - out bool isAnalyzerSuppressed) - { - Debug.Assert(isActiveDocument || isOpenDocument || isGeneratedRazorDocument); - - isAnalyzerSuppressed = false; - - try - { - var state = stateSet.GetOrCreateActiveFileState(document.Id); - var existingData = state.GetAnalysisData(kind); - - if (existingData.Version == version) - { - return (state, existingData); - } - - // Check whether analyzer is suppressed for project or document. - // If so, we set the flag indicating that the client can skip analysis for this document. - // Regardless of whether or not the analyzer is suppressed for project or document, - // we return null to indicate that no diagnostics are cached for this document for the given version. - isAnalyzerSuppressed = !DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(stateSet.Analyzer, document.Project, GlobalOptions) || - !IsAnalyzerEnabledForDocument(stateSet.Analyzer, existingData, analysisScope, compilerDiagnosticsScope, - isActiveDocument, isVisibleDocument, isOpenDocument, isGeneratedRazorDocument); - return (state, null); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - - static bool IsAnalyzerEnabledForDocument( - DiagnosticAnalyzer analyzer, - DocumentAnalysisData previousData, - BackgroundAnalysisScope analysisScope, - CompilerDiagnosticsScope compilerDiagnosticsScope, - bool isActiveDocument, - bool isVisibleDocument, - bool isOpenDocument, - bool isGeneratedRazorDocument) - { - Debug.Assert(!isActiveDocument || isOpenDocument || isGeneratedRazorDocument); - - if (isGeneratedRazorDocument) - { - // This is a generated Razor document, and they always want all analyzer diagnostics. - return true; - } - - if (analyzer.IsCompilerAnalyzer()) - { - return compilerDiagnosticsScope switch - { - // Compiler diagnostics are disabled for all documents. - CompilerDiagnosticsScope.None => false, - - // Compiler diagnostics are enabled for visible documents and open documents which had errors/warnings in prior snapshot. - CompilerDiagnosticsScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics => IsVisibleDocumentOrOpenDocumentWithPriorReportedVisibleDiagnostics(isVisibleDocument, isOpenDocument, previousData), - - // Compiler diagnostics are enabled for all open documents. - CompilerDiagnosticsScope.OpenFiles => isOpenDocument, - - // Compiler diagnostics are enabled for all documents. - CompilerDiagnosticsScope.FullSolution => true, - - _ => throw ExceptionUtilities.UnexpectedValue(analysisScope) - }; - } - else - { - return analysisScope switch - { - // Analyzers are disabled for all documents. - BackgroundAnalysisScope.None => false, - - // Analyzers are enabled for visible documents and open documents which had errors/warnings in prior snapshot. - BackgroundAnalysisScope.VisibleFilesAndOpenFilesWithPreviouslyReportedDiagnostics => IsVisibleDocumentOrOpenDocumentWithPriorReportedVisibleDiagnostics(isVisibleDocument, isOpenDocument, previousData), - - // Analyzers are enabled for all open documents. - BackgroundAnalysisScope.OpenFiles => isOpenDocument, - - // Analyzers are enabled for all documents. - BackgroundAnalysisScope.FullSolution => true, - - _ => throw ExceptionUtilities.UnexpectedValue(analysisScope) - }; - } - } - - static bool IsVisibleDocumentOrOpenDocumentWithPriorReportedVisibleDiagnostics( - bool isVisibleDocument, - bool isOpenDocument, - DocumentAnalysisData previousData) - { - if (isVisibleDocument) - return true; - - return isOpenDocument - && previousData.Items.Any(static d => d.Severity is DiagnosticSeverity.Error or DiagnosticSeverity.Warning or DiagnosticSeverity.Info); - } - } - - /// - /// Computes all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer). - /// - private static async Task ComputeDocumentAnalysisDataAsync( - DocumentAnalysisExecutor executor, DiagnosticAnalyzer analyzer, ActiveFileState state, bool logTelemetry, CancellationToken cancellationToken) - { - var kind = executor.AnalysisScope.Kind; - var document = executor.AnalysisScope.TextDocument; - - // get log title and functionId - GetLogFunctionIdAndTitle(kind, out var functionId, out var title); - - var logLevel = logTelemetry ? LogLevel.Information : LogLevel.Trace; - using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, analyzer, cancellationToken, logLevel: logLevel)) - { - try - { - var diagnostics = await executor.ComputeDiagnosticsAsync(analyzer, cancellationToken).ConfigureAwait(false); - - // this is no-op in product. only run in test environment - Logger.Log(functionId, (t, d, a, ds) => $"{GetDocumentLogMessage(t, d, a)}, {string.Join(Environment.NewLine, ds)}", - title, document, analyzer, diagnostics); - - var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); - var existingData = state.GetAnalysisData(kind); - var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - - // we only care about local diagnostics - return new DocumentAnalysisData(version, text.Lines.Count, existingData.Items, diagnostics.ToImmutableArrayOrEmpty()); - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) - { - throw ExceptionUtilities.Unreachable(); - } - } - } - /// /// Return all diagnostics that belong to given project for the given StateSets (analyzers) either from cache or by calculating them /// @@ -566,22 +411,5 @@ private void UpdateAnalyzerTelemetryData(ImmutableDictionary LoadInitialProjectAnalysisDataAsync return builder.ToResult(); } - private ValueTask AddToInMemoryStorageAsync( + private void AddToInMemoryStorage( VersionStamp serializerVersion, Project project, TextDocument? document, object key, string stateKey, ImmutableArray diagnostics) { Contract.ThrowIfFalse(document == null || document.Project == project); // if serialization fail, hold it in the memory InMemoryStorage.Cache(_owner.Analyzer, (key, stateKey), new CacheEntry(serializerVersion, diagnostics)); - return default; } private async ValueTask TryGetDiagnosticsFromInMemoryStorageAsync(VersionStamp serializerVersion, TextDocument document, Builder builder, CancellationToken cancellationToken) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index d3b1751751e4d..fe01283879f67 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -127,70 +127,6 @@ public IEnumerable GetOrCreateStateSets(Project project) return null; } - /// - /// Return s that are added as the given 's AnalyzerReferences. - /// This will never create new but will return ones already created. - /// - public ImmutableArray CreateBuildOnlyProjectStateSet(Project project) - { - var projectStateSets = project.SupportsCompilation - ? GetOrUpdateProjectStateSets(project) - : ProjectAnalyzerStateSets.Default; - var hostStateSets = GetOrCreateHostStateSets(project, projectStateSets); - - if (!project.SupportsCompilation) - { - // languages which don't use our compilation model but diagnostic framework, - // all their analyzer should be host analyzers. return all host analyzers - // for the language - return hostStateSets.OrderedStateSets; - } - - var hostStateSetMap = hostStateSets.StateSetMap; - - // create project analyzer reference identity map - var projectAnalyzerReferenceIds = project.AnalyzerReferences.Select(r => r.Id).ToSet(); - - // create build only stateSet array - var stateSets = ImmutableArray.CreateBuilder(); - - // include compiler analyzer in build only state, if available - StateSet? compilerStateSet = null; - var hostAnalyzers = project.Solution.SolutionState.Analyzers; - var compilerAnalyzer = hostAnalyzers.GetCompilerDiagnosticAnalyzer(project.Language); - if (compilerAnalyzer != null && hostStateSetMap.TryGetValue(compilerAnalyzer, out compilerStateSet)) - { - stateSets.Add(compilerStateSet); - } - - // now add all project analyzers - stateSets.AddRange(projectStateSets.StateSetMap.Values); - - // now add analyzers that exist in both host and project - var hostAnalyzersById = hostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(project.Language); - foreach (var (identity, analyzers) in hostAnalyzersById) - { - if (!projectAnalyzerReferenceIds.Contains(identity)) - { - // it is from host analyzer package rather than project analyzer reference - // which build doesn't have - continue; - } - - // if same analyzer exists both in host (vsix) and in analyzer reference, - // we include it in build only analyzer. - foreach (var analyzer in analyzers) - { - if (hostStateSetMap.TryGetValue(analyzer, out var stateSet) && stateSet != compilerStateSet) - { - stateSets.Add(stateSet); - } - } - } - - return stateSets.ToImmutable(); - } - public bool OnProjectRemoved(IEnumerable stateSets, ProjectId projectId) { var removed = false; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 4ee7e585fd01f..1ddadcfbd2bf1 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -5,14 +5,11 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -27,26 +24,17 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 /// internal partial class DiagnosticIncrementalAnalyzer { - private readonly int _correlationId; private readonly DiagnosticAnalyzerTelemetry _telemetry = new(); private readonly StateManager _stateManager; private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; - private readonly IDocumentTrackingService _documentTrackingService; private readonly IncrementalMemberEditAnalyzer _incrementalMemberEditAnalyzer = new(); -#if NETSTANDARD - private ConditionalWeakTable _projectCompilationsWithAnalyzers = new(); -#else - private readonly ConditionalWeakTable _projectCompilationsWithAnalyzers = []; -#endif - internal DiagnosticAnalyzerService AnalyzerService { get; } internal Workspace Workspace { get; } [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] public DiagnosticIncrementalAnalyzer( DiagnosticAnalyzerService analyzerService, - int correlationId, Workspace workspace, DiagnosticAnalyzerInfoCache analyzerInfoCache) { @@ -55,10 +43,6 @@ public DiagnosticIncrementalAnalyzer( AnalyzerService = analyzerService; Workspace = workspace; - _documentTrackingService = workspace.Services.GetRequiredService(); - - _correlationId = correlationId; - _stateManager = new StateManager(workspace, analyzerInfoCache); _stateManager.ProjectAnalyzerReferenceChanged += OnProjectAnalyzerReferenceChanged; @@ -78,108 +62,11 @@ private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerRe return; } - // events will be automatically serialized. - var project = e.Project; - var stateSets = e.Removed; - // make sure we drop cache related to the analyzers - foreach (var stateSet in stateSets) - { + foreach (var stateSet in e.Removed) stateSet.OnRemoved(); - } - - ClearAllDiagnostics(stateSets, project.Id); } - private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId projectId) - { - AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => - { - using var _ = PooledHashSet.GetInstance(out var documentSet); - using var argsBuilder = TemporaryArray.Empty; - - foreach (var stateSet in stateSets) - { - Debug.Assert(documentSet.Count == 0); - - stateSet.CollectDocumentsWithDiagnostics(projectId, documentSet); - - // PERF: don't fire events for ones that we dont have any diagnostics on - if (documentSet.Count > 0) - { - AddProjectDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), stateSet, projectId, documentSet, handleActiveFile: true); - documentSet.Clear(); - } - } - - raiseEvents(argsBuilder.ToImmutableAndClear()); - }); - } - - private void AddDiagnosticsCreatedArgs( - ref TemporaryArray builder, - Project project, DiagnosticAnalyzer analyzer, ImmutableArray items) - { - Contract.ThrowIfFalse(project.Solution.Workspace == Workspace); - - builder.Add(DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(analyzer, project.Id, AnalysisKind.NonLocal), - project.Solution.Workspace, - project.Solution, - project.Id, - documentId: null, - diagnostics: items)); - } - - private void AddDiagnosticsRemovedArgs( - ref TemporaryArray builder, - ProjectId projectId, Solution? solution, DiagnosticAnalyzer analyzer) - { - Contract.ThrowIfFalse(solution == null || solution.Workspace == Workspace); - - builder.Add(DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(analyzer, projectId, AnalysisKind.NonLocal), - Workspace, - solution, - projectId, - documentId: null)); - } - - private void AddDiagnosticsCreatedArgs( - ref TemporaryArray builder, - TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray items) - { - Contract.ThrowIfFalse(document.Project.Solution.Workspace == Workspace); - - builder.Add(DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(analyzer, document.Id, kind), - document.Project.Solution.Workspace, - document.Project.Solution, - document.Project.Id, - document.Id, - items)); - } - - private void AddDiagnosticsRemovedArgs( - ref TemporaryArray builder, - DocumentId documentId, Solution? solution, DiagnosticAnalyzer analyzer, AnalysisKind kind) - { - Contract.ThrowIfFalse(solution == null || solution.Workspace == Workspace); - - builder.Add(DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(analyzer, documentId, kind), - Workspace, - solution, - documentId.ProjectId, - documentId)); - } - - private static object CreateId(DiagnosticAnalyzer analyzer, DocumentId documentId, AnalysisKind kind) - => new LiveDiagnosticUpdateArgsId(analyzer, documentId, kind); - - private static object CreateId(DiagnosticAnalyzer analyzer, ProjectId projectId, AnalysisKind kind) - => new LiveDiagnosticUpdateArgsId(analyzer, projectId, kind); - public static Task GetDiagnosticVersionAsync(Project project, CancellationToken cancellationToken) => project.GetDependentVersionAsync(cancellationToken); @@ -196,22 +83,10 @@ private static DiagnosticAnalysisResult GetResultOrEmpty(ImmutableDictionary GetAnalyzersTestOnly(Project project) => _stateManager.GetOrCreateStateSets(project).Select(s => s.Analyzer); - private static string GetDocumentLogMessage(string title, TextDocument document, DiagnosticAnalyzer analyzer) - => $"{title}: ({document.Id}, {document.Project.Id}), ({analyzer})"; - private static string GetProjectLogMessage(Project project, ImmutableArray stateSets) => $"project: ({project.Id}), ({string.Join(Environment.NewLine, stateSets.Select(s => s.Analyzer.ToString()))})"; - private static string GetResetLogMessage(TextDocument document) - => $"document close/reset: ({document.FilePath ?? document.Name})"; - private static string GetOpenLogMessage(TextDocument document) => $"document open: ({document.FilePath ?? document.Name})"; - - private static string GetRemoveLogMessage(DocumentId id) - => $"document remove: {id.ToString()}"; - - private static string GetRemoveLogMessage(ProjectId id) - => $"project remove: {id.ToString()}"; } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs deleted file mode 100644 index eec6bc4234f96..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs +++ /dev/null @@ -1,211 +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.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 -{ - internal partial class DiagnosticIncrementalAnalyzer - { - public async ValueTask SynchronizeWithBuildAsync( - ImmutableDictionary> buildDiagnostics, - TaskQueue postBuildAndErrorListRefreshTaskQueue, - bool onBuildCompleted, - CancellationToken cancellationToken) - { - using (Logger.LogBlock(FunctionId.DiagnosticIncrementalAnalyzer_SynchronizeWithBuildAsync, LogSynchronizeWithBuild, buildDiagnostics, cancellationToken)) - { - DebugVerifyBuildDiagnostics(buildDiagnostics); - - var solution = Workspace.CurrentSolution; - - foreach (var (projectId, diagnostics) in buildDiagnostics) - { - cancellationToken.ThrowIfCancellationRequested(); - - var project = solution.GetProject(projectId); - if (project == null) - { - continue; - } - - var stateSets = _stateManager.CreateBuildOnlyProjectStateSet(project); - var newResult = CreateAnalysisResults(project, stateSets, diagnostics); - - // PERF: Save the diagnostics into in-memory cache on the main thread. - // Saving them into persistent storage is expensive, so we invoke that operation on a separate task queue - // to ensure faster error list refresh. - foreach (var stateSet in stateSets) - { - cancellationToken.ThrowIfCancellationRequested(); - - var state = stateSet.GetOrCreateProjectState(project.Id); - var result = GetResultOrEmpty(newResult, stateSet.Analyzer, project.Id, VersionStamp.Default); - - await state.SaveToInMemoryStorageAsync(project, result).ConfigureAwait(false); - } - - // Raise diagnostic updated events after the new diagnostics have been stored into the in-memory cache. - if (diagnostics.IsEmpty) - { - ClearAllDiagnostics(stateSets, projectId); - } - else - { - RaiseProjectDiagnosticsIfNeeded(project, stateSets, newResult); - } - } - - // Refresh live diagnostics after solution build completes. - if (onBuildCompleted) - { - // Enqueue re-analysis of active document with high-priority right away. - if (_documentTrackingService.GetActiveDocument(solution) is { } activeDocument) - { - AnalyzerService.Reanalyze(Workspace, projectIds: null, documentIds: ImmutableArray.Create(activeDocument.Id), highPriority: true); - } - - // Enqueue remaining re-analysis with normal priority on a separate task queue - // that will execute at the end of all the post build and error list refresh tasks. - _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuildAsync), () => - { - // Enqueue re-analysis of open documents. - AnalyzerService.Reanalyze(Workspace, projectIds: null, documentIds: Workspace.GetOpenDocumentIds(), highPriority: false); - - // Enqueue re-analysis of projects, if required. - foreach (var projectsByLanguage in solution.Projects.GroupBy(p => p.Language)) - { - if (GlobalOptions.IsFullSolutionAnalysisEnabled(projectsByLanguage.Key)) - { - AnalyzerService.Reanalyze(Workspace, projectsByLanguage.Select(p => p.Id), documentIds: null, highPriority: false); - } - } - }, cancellationToken); - } - } - } - - [Conditional("DEBUG")] - private static void DebugVerifyBuildDiagnostics(ImmutableDictionary> buildDiagnostics) - { - foreach (var diagnostic in buildDiagnostics.Values.SelectMany(v => v)) - { - Debug.Assert(diagnostic.IsBuildDiagnostic()); - } - } - - private ImmutableDictionary CreateAnalysisResults( - Project project, ImmutableArray stateSets, ImmutableArray diagnostics) - { - using var poolObject = SharedPools.Default>().GetPooledObject(); - - var lookup = diagnostics.ToLookup(d => d.Id); - - var builder = ImmutableDictionary.CreateBuilder(); - using var _ = PooledHashSet.GetInstance(out var existingDocumentsInStateSet); - foreach (var stateSet in stateSets) - { - var descriptors = DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(stateSet.Analyzer); - var liveDiagnostics = ConvertToLiveDiagnostics(lookup, descriptors, poolObject.Object); - - // Ensure that all documents with diagnostics in the previous state set are added to the result. - existingDocumentsInStateSet.Clear(); - stateSet.CollectDocumentsWithDiagnostics(project.Id, existingDocumentsInStateSet); - - builder.Add(stateSet.Analyzer, DiagnosticAnalysisResult.CreateFromBuild(project, liveDiagnostics, existingDocumentsInStateSet)); - } - - return builder.ToImmutable(); - } - - private static ImmutableArray ConvertToLiveDiagnostics( - ILookup lookup, ImmutableArray descriptors, HashSet seen) - { - if (lookup == null) - { - return []; - } - - ImmutableArray.Builder? builder = null; - foreach (var descriptor in descriptors) - { - // make sure we don't report same id to multiple different analyzers - if (!seen.Add(descriptor.Id)) - { - // TODO: once we have information where diagnostic came from, we probably don't need this. - continue; - } - - var items = lookup[descriptor.Id]; - if (items == null) - { - continue; - } - - builder ??= ImmutableArray.CreateBuilder(); - builder.AddRange(items.Select(d => CreateLiveDiagnostic(descriptor, d))); - } - - return builder == null ? [] : builder.ToImmutable(); - } - - private static DiagnosticData CreateLiveDiagnostic(DiagnosticDescriptor descriptor, DiagnosticData diagnostic) - { - return new DiagnosticData( - descriptor.Id, - descriptor.Category, - diagnostic.Message, - diagnostic.Severity, - descriptor.DefaultSeverity, - descriptor.IsEnabledByDefault, - diagnostic.WarningLevel, - descriptor.ImmutableCustomTags(), - diagnostic.Properties, - diagnostic.ProjectId, - diagnostic.DataLocation, - diagnostic.AdditionalLocations, - diagnostic.Language, - descriptor.Title.ToString(CultureInfo.CurrentUICulture), - descriptor.Description.ToString(CultureInfo.CurrentUICulture), - descriptor.HelpLinkUri, - isSuppressed: diagnostic.IsSuppressed); - } - - private static string LogSynchronizeWithBuild(ImmutableDictionary> map) - { - using var pooledObject = SharedPools.Default().GetPooledObject(); - var sb = pooledObject.Object; - - if (map.Count > 0) - { - foreach (var (projectId, diagnostics) in map) - { - sb.AppendLine($"{projectId}, Count: {diagnostics.Length}"); - - foreach (var diagnostic in diagnostics) - { - sb.AppendLine($" {diagnostic}"); - } - } - } - - return sb.ToString(); - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 187f528b30431..118e5133c645e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -3,29 +3,22 @@ // 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.ErrorReporting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) - => AnalyzeProjectAsync(project, forceAnalyzerRun: true, cancellationToken); - - private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, CancellationToken cancellationToken) + public async Task> ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken) { try { @@ -38,35 +31,29 @@ private async Task AnalyzeProjectAsync(Project project, bool forceAnalyzerRun, C // this is perf optimization. we cache these result since we know the result. (no diagnostics) var activeAnalyzers = stateSets .Select(s => s.Analyzer) - .Where(a => (forceAnalyzerRun || DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(a, project, GlobalOptions)) && !a.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions)); + .Where(a => !a.IsOpenFileOnly(ideOptions.CleanupOptions?.SimplifierOptions)); CompilationWithAnalyzers? compilationWithAnalyzers = null; - if (forceAnalyzerRun || GlobalOptions.IsFullSolutionAnalysisEnabled(project.Language)) - { - compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, activeAnalyzers, includeSuppressedDiagnostics: true, cancellationToken).ConfigureAwait(false); - } + compilationWithAnalyzers = await DocumentAnalysisExecutor.CreateCompilationWithAnalyzersAsync(project, ideOptions, activeAnalyzers, includeSuppressedDiagnostics: true, cancellationToken).ConfigureAwait(false); - var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, ideOptions, stateSets, forceAnalyzerRun, cancellationToken).ConfigureAwait(false); + var result = await GetProjectAnalysisDataAsync(compilationWithAnalyzers, project, ideOptions, stateSets, forceAnalyzerRun: true, cancellationToken).ConfigureAwait(false); + + using var _ = ArrayBuilder.GetInstance(out var diagnostics); // no cancellation after this point. - using var _ = ArrayBuilder.GetInstance(out var analyzedStateSetsBuilder); foreach (var stateSet in stateSets) { var state = stateSet.GetOrCreateProjectState(project.Id); if (result.TryGetResult(stateSet.Analyzer, out var analyzerResult)) { + diagnostics.AddRange(analyzerResult.GetAllDiagnostics()); await state.SaveToInMemoryStorageAsync(project, analyzerResult).ConfigureAwait(false); - analyzedStateSetsBuilder.Add(stateSet); } } - if (analyzedStateSetsBuilder.Count > 0) - { - var oldResult = result.OldResult ?? ImmutableDictionary.Empty; - RaiseProjectDiagnosticsIfNeeded(project, analyzedStateSetsBuilder.ToImmutable(), oldResult, result.Result); - } + return diagnostics.ToImmutable(); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { @@ -163,178 +150,6 @@ private bool IsCandidateForFullSolutionAnalysis(DiagnosticAnalyzer analyzer, Pro return descriptors.Any(static (d, arg) => d.GetEffectiveSeverity(arg.CompilationOptions, arg.analyzerConfigOptions?.AnalyzerOptions, arg.analyzerConfigOptions?.TreeOptions) != ReportDiagnostic.Hidden, (project.CompilationOptions, analyzerConfigOptions)); } - private void RaiseProjectDiagnosticsIfNeeded( - Project project, - IEnumerable stateSets, - ImmutableDictionary result) - { - RaiseProjectDiagnosticsIfNeeded(project, stateSets, ImmutableDictionary.Empty, result); - } - - private void RaiseProjectDiagnosticsIfNeeded( - Project project, - IEnumerable stateSets, - ImmutableDictionary oldResult, - ImmutableDictionary newResult) - { - if (oldResult.Count == 0 && newResult.Count == 0) - { - // there is nothing to update - return; - } - - AnalyzerService.RaiseBulkDiagnosticsUpdated(async raiseEvents => - { - using var argsBuilder = TemporaryArray.Empty; - foreach (var stateSet in stateSets) - { - var analyzer = stateSet.Analyzer; - - var oldAnalysisResult = GetResultOrEmpty(oldResult, analyzer, project.Id, VersionStamp.Default); - var newAnalysisResult = GetResultOrEmpty(newResult, analyzer, project.Id, VersionStamp.Default); - - // Perf - 4 different cases. - // upper 3 cases can be removed and it will still work. but this is hot path so if we can bail out - // without any allocations, that's better. - if (oldAnalysisResult.IsEmpty && newAnalysisResult.IsEmpty) - { - // nothing to do - continue; - } - - if (!oldAnalysisResult.IsEmpty && newAnalysisResult.IsEmpty) - { - RoslynDebug.Assert(oldAnalysisResult.DocumentIds != null); - - // remove old diagnostics - AddProjectDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), stateSet, oldAnalysisResult.ProjectId, oldAnalysisResult.DocumentIds, handleActiveFile: false); - continue; - } - - if (oldAnalysisResult.IsEmpty && !newAnalysisResult.IsEmpty) - { - // add new diagnostics - argsBuilder.AddRange(await CreateProjectDiagnosticsCreatedArgsAsync(project, stateSet, oldAnalysisResult, newAnalysisResult, CancellationToken.None).ConfigureAwait(false)); - continue; - } - - // both old and new has items in them. update existing items - RoslynDebug.Assert(oldAnalysisResult.DocumentIds != null); - RoslynDebug.Assert(newAnalysisResult.DocumentIds != null); - - // first remove ones no longer needed. - var documentsToRemove = oldAnalysisResult.DocumentIds.Except(newAnalysisResult.DocumentIds); - AddProjectDiagnosticsRemovedArgs(ref argsBuilder.AsRef(), stateSet, oldAnalysisResult.ProjectId, documentsToRemove, handleActiveFile: false); - - // next update or create new ones - argsBuilder.AddRange(await CreateProjectDiagnosticsCreatedArgsAsync(project, stateSet, oldAnalysisResult, newAnalysisResult, CancellationToken.None).ConfigureAwait(false)); - } - - raiseEvents(argsBuilder.ToImmutableAndClear()); - }); - } - - private void AddDocumentDiagnosticsArgsIfNeeded( - ref TemporaryArray builder, - TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, - DiagnosticAnalysisResult oldResult, DiagnosticAnalysisResult newResult) - { - // if our old result is from build and we don't have actual data, don't try micro-optimize and always refresh diagnostics. - // most of time, we don't actually load or hold the old data in memory from persistent storage due to perf reasons. - // - // we need this special behavior for errors from build since unlike live errors, we don't know whether errors - // from build is for syntax, semantic or others. due to that, we blindly mark them as semantic errors (most common type of errors from build) - // - // that can sometime cause issues. for example, if the error turns out to be syntax error (live) then we at the end fail to de-dup. - // but since this optimization saves us a lot of refresh between live errors analysis we want to disable this only in this condition. - var forceUpdate = oldResult.FromBuild && oldResult.IsAggregatedForm; - - var oldItems = oldResult.GetDocumentDiagnostics(document.Id, kind); - var newItems = newResult.GetDocumentDiagnostics(document.Id, kind); - - AddDocumentDiagnosticsArgsIfNeeded(ref builder, document, analyzer, kind, oldItems, newItems, forceUpdate); - } - - private void AddDocumentDiagnosticsArgsIfNeeded( - ref TemporaryArray builder, - TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, - ImmutableArray oldItems, ImmutableArray newItems, - bool forceUpdate) - { - if (!forceUpdate && oldItems.IsEmpty && newItems.IsEmpty) - { - // there is nothing to update - return; - } - - AddDiagnosticsCreatedArgs(ref builder, document, analyzer, kind, newItems); - } - - private async Task> CreateProjectDiagnosticsCreatedArgsAsync(Project project, StateSet stateSet, DiagnosticAnalysisResult oldAnalysisResult, DiagnosticAnalysisResult newAnalysisResult, CancellationToken cancellationToken) - { - RoslynDebug.Assert(newAnalysisResult.DocumentIds != null); - - using var argsBuilder = TemporaryArray.Empty; - foreach (var documentId in newAnalysisResult.DocumentIds) - { - var document = project.GetTextDocument(documentId); - - // If we couldn't find a normal document, and all features are enabled for source generated documents, - // attempt to locate a matching source generated document in the project. - if (document is null - && project.Solution.Services.GetService()?.EnableDiagnosticsInSourceGeneratedFiles == true) - { - document = await project.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); - } - - if (document == null) - { - // it can happen with build synchronization since, in build case, - // we don't have actual snapshot (we have no idea what sources out of proc build has picked up) - // so we might be out of sync. - // example of such cases will be changing anything about solution while building is going on. - // it can be user explicit actions such as unloading project, deleting a file, but also it can be - // something project system or roslyn workspace does such as populating workspace right after - // solution is loaded. - continue; - } - - AddDocumentDiagnosticsArgsIfNeeded(ref argsBuilder.AsRef(), document, stateSet.Analyzer, AnalysisKind.NonLocal, oldAnalysisResult, newAnalysisResult); - - // we don't raise events for active file. it will be taken cared by active file analysis - if (stateSet.IsActiveFile(documentId)) - { - continue; - } - - AddDocumentDiagnosticsArgsIfNeeded(ref argsBuilder.AsRef(), document, stateSet.Analyzer, AnalysisKind.Syntax, oldAnalysisResult, newAnalysisResult); - AddDocumentDiagnosticsArgsIfNeeded(ref argsBuilder.AsRef(), document, stateSet.Analyzer, AnalysisKind.Semantic, oldAnalysisResult, newAnalysisResult); - } - - AddDiagnosticsCreatedArgs(ref argsBuilder.AsRef(), project, stateSet.Analyzer, newAnalysisResult.GetOtherDiagnostics()); - - return argsBuilder.ToImmutableAndClear(); - } - - private void AddProjectDiagnosticsRemovedArgs(ref TemporaryArray builder, StateSet stateSet, ProjectId projectId, IEnumerable documentIds, bool handleActiveFile) - { - foreach (var documentId in documentIds) - { - AddDiagnosticsRemovedArgs(ref builder, documentId, solution: null, stateSet.Analyzer, AnalysisKind.NonLocal); - - // we don't raise events for active file. it will be taken care of by active file analysis - if (!handleActiveFile && stateSet.IsActiveFile(documentId)) - { - continue; - } - - AddDiagnosticsRemovedArgs(ref builder, documentId, solution: null, stateSet.Analyzer, AnalysisKind.Syntax); - AddDiagnosticsRemovedArgs(ref builder, documentId, solution: null, stateSet.Analyzer, AnalysisKind.Semantic); - } - - AddDiagnosticsRemovedArgs(ref builder, projectId, solution: null, stateSet.Analyzer); - } - public TestAccessor GetTestAccessor() => new(this); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs deleted file mode 100644 index 43521b1561ee9..0000000000000 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/IDiagnosticService.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; - -namespace Microsoft.CodeAnalysis.Diagnostics -{ - /// - /// Aggregates events from various diagnostic sources. - /// - internal interface IDiagnosticService - { - /// - /// Event to get notified as new diagnostics are discovered by IDiagnosticUpdateSource - /// - /// Notifications for this event are serialized to preserve order. - /// However, individual event notifications may occur on any thread. - /// - event EventHandler> DiagnosticsUpdated; - } -} diff --git a/src/Features/LanguageServer/Protocol/Features/Options/ClassificationOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/ClassificationOptionsStorage.cs index 87d9faa3241fd..194765e18320e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/ClassificationOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/ClassificationOptionsStorage.cs @@ -12,6 +12,7 @@ public static ClassificationOptions GetClassificationOptions(this IOptionsReader => new() { ClassifyReassignedVariables = globalOptions.GetOption(ClassifyReassignedVariables, language), + ClassifyObsoleteSymbols = globalOptions.GetOption(ClassifyObsoleteSymbols, language), ColorizeRegexPatterns = globalOptions.GetOption(ColorizeRegexPatterns, language), ColorizeJsonPatterns = globalOptions.GetOption(ColorizeJsonPatterns, language), // ForceFrozenPartialSemanticsForCrossProcessOperations not stored in global options @@ -23,6 +24,9 @@ public static OptionsProvider GetClassificationOptionsPro public static PerLanguageOption2 ClassifyReassignedVariables = new("dotnet_classify_reassigned_variables", ClassificationOptions.Default.ClassifyReassignedVariables); + public static PerLanguageOption2 ClassifyObsoleteSymbols = + new("dotnet_classify_obsolete_symbols", ClassificationOptions.Default.ClassifyObsoleteSymbols); + public static PerLanguageOption2 ColorizeRegexPatterns = new("dotnet_colorize_regex_patterns", ClassificationOptions.Default.ColorizeRegexPatterns); diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/AbstractLspCompletionResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/AbstractLspCompletionResultCreationService.cs index 8cdfd83df2e1f..7f3ceaa0a5795 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/AbstractLspCompletionResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/AbstractLspCompletionResultCreationService.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -121,6 +122,7 @@ internal abstract class AbstractLspCompletionResultCreationService : ILspComplet lspItem.FilterText = item.FilterText; lspItem.Kind = GetCompletionKind(item.Tags, capabilityHelper.SupportedItemKinds); + lspItem.Tags = GetCompletionTags(item.Tags, capabilityHelper.SupportedItemTags); lspItem.Preselect = item.Rules.MatchPriority == MatchPriority.Preselect; if (lspVSClientCapability) @@ -179,6 +181,37 @@ static LSP.CompletionItemKind GetCompletionKind( return LSP.CompletionItemKind.Text; } + static LSP.CompletionItemTag[]? GetCompletionTags( + ImmutableArray tags, + ISet supportedClientTags) + { + using var result = TemporaryArray.Empty; + + foreach (var tag in tags) + { + if (ProtocolConversions.RoslynTagToCompletionItemTags.TryGetValue(tag, out var completionItemTags)) + { + // Always at least pick the core tag provided. + var lspTag = completionItemTags[0]; + + // If better kinds are preferred, return them if the client supports them. + for (var i = 1; i < completionItemTags.Length; i++) + { + var preferredTag = completionItemTags[i]; + if (supportedClientTags.Contains(preferredTag)) + lspTag = preferredTag; + } + + result.Add(lspTag); + } + } + + if (result.Count == 0) + return null; + + return [.. result.ToImmutableAndClear()]; + } + static string[] GetCommitCharacters( CompletionItem item, Dictionary, string[]> currentRuleCache) diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs index bd565c39807ac..7bf5905039f3d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionCapabilityHelper.cs @@ -30,6 +30,7 @@ internal sealed class CompletionCapabilityHelper public bool SupportSnippets { get; } public bool SupportsMarkdownDocumentation { get; } public ISet SupportedItemKinds { get; } + public ISet SupportedItemTags { get; } public CompletionCapabilityHelper(ClientCapabilities clientCapabilities) { @@ -42,6 +43,7 @@ public CompletionCapabilityHelper(ClientCapabilities clientCapabilities) SupportCompletionListData = _completionSetting?.CompletionListSetting?.ItemDefaults?.Contains(DataPropertyName) == true; SupportDefaultCommitCharacters = _completionSetting?.CompletionListSetting?.ItemDefaults?.Contains(CommitCharactersPropertyName) == true; SupportedItemKinds = _completionSetting?.CompletionItemKind?.ValueSet?.ToSet() ?? SpecializedCollections.EmptySet(); + SupportedItemTags = _completionSetting?.CompletionItem?.TagSupport?.ValueSet?.ToSet() ?? SpecializedCollections.EmptySet(); // internal VS LSP if (clientCapabilities.HasVisualStudioLspCapability()) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs index 637fa491c621f..5e632744ee440 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/DocumentDiagnosticSource.cs @@ -34,7 +34,7 @@ public override async Task> GetDiagnosticsAsync( // Add cached Copilot diagnostics when computing analyzer semantic diagnostics. if (DiagnosticKind == DiagnosticKind.AnalyzerSemantic) { - var copilotDiagnostics = await Document.GetCachedCopilotDiagnosticsAsync(cancellationToken).ConfigureAwait(false); + var copilotDiagnostics = await Document.GetCachedCopilotDiagnosticsAsync(span: null, cancellationToken).ConfigureAwait(false); allSpanDiagnostics = allSpanDiagnostics.AddRange(copilotDiagnostics); } diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs b/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs index 8a24ab46cfa87..1ff18f536c1e3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs @@ -66,6 +66,7 @@ public void UpdateTelemetryData( m[TelemetryLogging.KeyValue] = queuedDuration.Milliseconds; m[TelemetryLogging.KeyMetricName] = "TimeInQueue"; m["server"] = _serverTypeName; + m["method"] = methodName; })); TelemetryLogging.LogAggregated(FunctionId.LSP_RequestDuration, KeyValueLogMessage.Create(m => diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs index a3d07daa66148..428806dc57f70 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs @@ -320,16 +320,21 @@ private static int ComputeNextToken( if (classificationType == ClassificationTypeNames.StaticSymbol) { // 4. Token modifiers - each set bit will be looked up in SemanticTokensLegend.tokenModifiers - modifierBits = TokenModifiers.Static; + modifierBits |= TokenModifiers.Static; } else if (classificationType == ClassificationTypeNames.ReassignedVariable) { // 5. Token modifiers - each set bit will be looked up in SemanticTokensLegend.tokenModifiers - modifierBits = TokenModifiers.ReassignedVariable; + modifierBits |= TokenModifiers.ReassignedVariable; + } + else if (classificationType == ClassificationTypeNames.ObsoleteSymbol) + { + // 6. Token modifiers - each set bit will be looked up in SemanticTokensLegend.tokenModifiers + modifierBits |= TokenModifiers.Deprecated; } else { - // 6. Token type - looked up in SemanticTokensLegend.tokenTypes (language server defined mapping + // 7. Token type - looked up in SemanticTokensLegend.tokenTypes (language server defined mapping // from integer to LSP token types). tokenTypeIndex = GetTokenTypeIndex(classificationType); } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs index 94cdb33c0955e..752ec12df2eeb 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs @@ -130,7 +130,8 @@ public static SemanticTokensSchema LegacyTokensSchemaForLSIF [ // This must be in the same order as SemanticTokens.TokenModifiers, but skip the "None" item SemanticTokenModifiers.Static, - nameof(SemanticTokens.TokenModifiers.ReassignedVariable) + nameof(SemanticTokens.TokenModifiers.ReassignedVariable), + SemanticTokenModifiers.Deprecated, ]; } } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/TokenModifiers.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/TokenModifiers.cs index c93cf9c6c35ce..b5f820e1d18a7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/TokenModifiers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/TokenModifiers.cs @@ -16,5 +16,6 @@ internal enum TokenModifiers None = 0, Static = 1, ReassignedVariable = 2, + Deprecated = 4, } } diff --git a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index df5d1f713bcd7..6db127bd2f722 100644 --- a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -81,6 +81,7 @@ + diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs index acd63d0657304..f416ae767d3d1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs @@ -52,6 +52,17 @@ public CompletionItemKind Kind set; } = CompletionItemKind.None; + /// + /// Tags for this completion item. + /// + [DataMember(Name = "tags")] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public CompletionItemTag[]? Tags + { + get; + set; + } + /// /// Gets or sets the completion detail. /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs index 8369864bfd38f..2dbe0839d6d76 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs @@ -176,6 +176,12 @@ private static void WriteCompletionItem(JsonWriter writer, CompletionItem comple writer.WritePropertyName("kind"); writer.WriteValue(completionItem.Kind); + if (completionItem.Tags != null) + { + writer.WritePropertyName("tags"); + serializer.Serialize(writer, completionItem.Tags); + } + if (completionItem.Detail != null) { writer.WritePropertyName("detail"); diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index 1a1ef3f6833d0..3c84bd75aeb67 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -203,7 +203,7 @@ public void UpdateTrackedDocument(Uri uri, SourceText newSourceText) // Ensure we have the latest lsp solutions var updatedSolutions = await GetLspSolutionsAsync(cancellationToken).ConfigureAwait(false); - var (hostWorkspace, hostWorkspaceSolution, isForked) = updatedSolutions.FirstOrDefault(lspSolution => lspSolution.Solution.WorkspaceKind == WorkspaceKind.Host); + var (hostWorkspace, hostWorkspaceSolution, isForked) = updatedSolutions.FirstOrDefault(lspSolution => lspSolution.Solution.WorkspaceKind is WorkspaceKind.Host); _requestTelemetryLogger.UpdateUsedForkedSolutionCounter(isForked); return (hostWorkspace, hostWorkspaceSolution); diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceRegistrationEventListener.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceRegistrationEventListener.cs index 31071d7b54cc4..d9b81548650c5 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceRegistrationEventListener.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceRegistrationEventListener.cs @@ -14,7 +14,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer; WorkspaceKind.Host, WorkspaceKind.MiscellaneousFiles, WorkspaceKind.MetadataAsSource, - WorkspaceKind.Interactive), Shared] + WorkspaceKind.Interactive, + WorkspaceKind.SemanticSearch), Shared] internal sealed class LspWorkspaceRegistrationEventListener : IEventListener, IEventListenerStoppable { private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs index 97d60c6a0957c..00dfa0293382c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs @@ -496,6 +496,79 @@ public async Task TestUsingServerDefaultCommitCharacters(bool mutatingLspWorkspa } } + [Theory] + [CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/26488")] + public async Task TestCompletionForObsoleteSymbol(bool mutatingLspWorkspace) + { + var markup = + """ + using System; + + [Obsolete] + class ObsoleteType; + + class A + { + void M() + { + ObsoleteType{|caret:|} + } + } + """; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, DefaultClientCapabilities); + var completionParams = CreateCompletionParams( + testLspServer.GetLocations("caret").Single(), + invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, + triggerCharacter: "\0", + triggerKind: LSP.CompletionTriggerKind.Invoked); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + + var completionResult = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, completionParams, CancellationToken.None).ConfigureAwait(false); + Assert.NotNull(completionResult.ItemDefaults.EditRange); + Assert.NotNull(completionResult.ItemDefaults.Data); + Assert.NotNull(completionResult.ItemDefaults.CommitCharacters); + + var actualItem = completionResult.Items.First(i => i.Label == "ObsoleteType"); + Assert.Null(actualItem.LabelDetails); + Assert.Null(actualItem.SortText); + Assert.Equal(CompletionItemKind.Class, actualItem.Kind); + Assert.Equal([CompletionItemTag.Deprecated], actualItem.Tags); + Assert.Null(actualItem.FilterText); + Assert.Null(actualItem.TextEdit); + Assert.Null(actualItem.TextEditText); + Assert.Null(actualItem.AdditionalTextEdits); + Assert.Null(actualItem.Command); + Assert.Null(actualItem.CommitCharacters); + Assert.Null(actualItem.Data); + Assert.Null(actualItem.Detail); + Assert.Null(actualItem.Documentation); + + actualItem.Data = completionResult.ItemDefaults.Data; + + var resolvedItem = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionResolveName, actualItem, CancellationToken.None).ConfigureAwait(false); + Assert.Null(resolvedItem.LabelDetails); + Assert.Null(resolvedItem.SortText); + Assert.Equal(CompletionItemKind.Class, resolvedItem.Kind); + Assert.Equal([CompletionItemTag.Deprecated], resolvedItem.Tags); + + Assert.Null(resolvedItem.AdditionalTextEdits); + Assert.Null(resolvedItem.FilterText); + Assert.Null(resolvedItem.TextEdit); + Assert.Null(resolvedItem.TextEditText); + Assert.Null(resolvedItem.Command); + Assert.Null(resolvedItem.Detail); + + var expectedDocumentation = new MarkupContent() + { + Kind = LSP.MarkupKind.PlainText, + Value = "[deprecated] class ObsoleteType" + }; + AssertJsonEquals(resolvedItem.Documentation, expectedDocumentation); + } + private sealed class CSharpLspMockCompletionService : CompletionService { private CSharpLspMockCompletionService(SolutionServices services, IAsynchronousOperationListenerProvider listenerProvider) : base(services, listenerProvider) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 61929ec437f86..87db8cfbcc8f6 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -86,7 +86,7 @@ public static Generator CreateAndWriteCapabilitiesVertex(ILsifJsonWriter lsifJso DocumentSymbolProvider, FoldingRangeProvider, DiagnosticProvider, - new SemanticTokensCapabilities(SemanticTokensSchema.LegacyTokensSchemaForLSIF.AllTokenTypes, [SemanticTokenModifiers.Static])); + new SemanticTokensCapabilities(SemanticTokensSchema.LegacyTokensSchemaForLSIF.AllTokenTypes, [SemanticTokenModifiers.Static, SemanticTokenModifiers.Deprecated])); generator._lsifJsonWriter.Write(capabilitiesVertex); return generator; } diff --git a/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb b/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb index 2db321450b74f..5b01bbd5725c8 100644 --- a/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb +++ b/src/Features/Lsif/GeneratorTest/OutputFormatTests.vb @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests , openDocuments:=False, composition:=TestLsifOutput.TestComposition), jsonWriter) AssertEx.EqualOrDiff( -"{""hoverProvider"":true,""declarationProvider"":false,""definitionProvider"":true,""referencesProvider"":true,""typeDefinitionProvider"":false,""documentSymbolProvider"":true,""foldingRangeProvider"":true,""diagnosticProvider"":false,""semanticTokensProvider"":{""tokenTypes"":[""namespace"",""type"",""class"",""enum"",""interface"",""struct"",""typeParameter"",""parameter"",""variable"",""property"",""enumMember"",""event"",""function"",""method"",""macro"",""keyword"",""modifier"",""comment"",""string"",""number"",""regexp"",""operator"",""class name"",""constant name"",""delegate name"",""enum member name"",""enum name"",""event name"",""excluded code"",""extension method name"",""field name"",""interface name"",""json - array"",""json - comment"",""json - constructor name"",""json - keyword"",""json - number"",""json - object"",""json - operator"",""json - property name"",""json - punctuation"",""json - string"",""json - text"",""keyword - control"",""label name"",""local name"",""method name"",""module name"",""namespace name"",""operator - overloaded"",""parameter name"",""preprocessor keyword"",""preprocessor text"",""property name"",""punctuation"",""record class name"",""record struct name"",""regex - alternation"",""regex - anchor"",""regex - character class"",""regex - comment"",""regex - grouping"",""regex - other escape"",""regex - quantifier"",""regex - self escaped character"",""regex - text"",""roslyn test code markdown"",""string - escape character"",""string - verbatim"",""struct name"",""text"",""type parameter name"",""whitespace"",""xml doc comment - attribute name"",""xml doc comment - attribute quotes"",""xml doc comment - attribute value"",""xml doc comment - cdata section"",""xml doc comment - comment"",""xml doc comment - delimiter"",""xml doc comment - entity reference"",""xml doc comment - name"",""xml doc comment - processing instruction"",""xml doc comment - text"",""xml literal - attribute name"",""xml literal - attribute quotes"",""xml literal - attribute value"",""xml literal - cdata section"",""xml literal - comment"",""xml literal - delimiter"",""xml literal - embedded expression"",""xml literal - entity reference"",""xml literal - name"",""xml literal - processing instruction"",""xml literal - text""],""tokenModifiers"":[""static""]},""id"":1,""type"":""vertex"",""label"":""capabilities""} +"{""hoverProvider"":true,""declarationProvider"":false,""definitionProvider"":true,""referencesProvider"":true,""typeDefinitionProvider"":false,""documentSymbolProvider"":true,""foldingRangeProvider"":true,""diagnosticProvider"":false,""semanticTokensProvider"":{""tokenTypes"":[""namespace"",""type"",""class"",""enum"",""interface"",""struct"",""typeParameter"",""parameter"",""variable"",""property"",""enumMember"",""event"",""function"",""method"",""macro"",""keyword"",""modifier"",""comment"",""string"",""number"",""regexp"",""operator"",""class name"",""constant name"",""delegate name"",""enum member name"",""enum name"",""event name"",""excluded code"",""extension method name"",""field name"",""interface name"",""json - array"",""json - comment"",""json - constructor name"",""json - keyword"",""json - number"",""json - object"",""json - operator"",""json - property name"",""json - punctuation"",""json - string"",""json - text"",""keyword - control"",""label name"",""local name"",""method name"",""module name"",""namespace name"",""operator - overloaded"",""parameter name"",""preprocessor keyword"",""preprocessor text"",""property name"",""punctuation"",""record class name"",""record struct name"",""regex - alternation"",""regex - anchor"",""regex - character class"",""regex - comment"",""regex - grouping"",""regex - other escape"",""regex - quantifier"",""regex - self escaped character"",""regex - text"",""roslyn test code markdown"",""string - escape character"",""string - verbatim"",""struct name"",""text"",""type parameter name"",""whitespace"",""xml doc comment - attribute name"",""xml doc comment - attribute quotes"",""xml doc comment - attribute value"",""xml doc comment - cdata section"",""xml doc comment - comment"",""xml doc comment - delimiter"",""xml doc comment - entity reference"",""xml doc comment - name"",""xml doc comment - processing instruction"",""xml doc comment - text"",""xml literal - attribute name"",""xml literal - attribute quotes"",""xml literal - attribute value"",""xml literal - cdata section"",""xml literal - comment"",""xml literal - delimiter"",""xml literal - embedded expression"",""xml literal - entity reference"",""xml literal - name"",""xml literal - processing instruction"",""xml literal - text""],""tokenModifiers"":[""static"",""deprecated""]},""id"":1,""type"":""vertex"",""label"":""capabilities""} {""kind"":""csharp"",""resource"":""file:///Z:/%EE%89%9B/TestProject.csproj"",""name"":""TestProject"",""id"":2,""type"":""vertex"",""label"":""project""} {""kind"":""begin"",""scope"":""project"",""data"":2,""id"":3,""type"":""vertex"",""label"":""$event""} {""uri"":""file:///Z:/%EE%89%9B/a.cs"",""languageId"":""csharp"",""id"":4,""type"":""vertex"",""label"":""document""} @@ -168,7 +168,8 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ""xml literal - text"" ], ""tokenModifiers"": [ - ""static"" + ""static"", + ""deprecated"" ] }, ""id"": 1, diff --git a/src/Features/Test/EditAndContinue/ActiveStatementsMapTests.cs b/src/Features/Test/EditAndContinue/ActiveStatementsMapTests.cs index afc1d49fcf948..d3bea48ce86cc 100644 --- a/src/Features/Test/EditAndContinue/ActiveStatementsMapTests.cs +++ b/src/Features/Test/EditAndContinue/ActiveStatementsMapTests.cs @@ -151,7 +151,7 @@ ManagedActiveStatementDebugInfo CreateInfo(int startLine, int startColumn, int e "[120..124) -> (4,0)-(4,4) #2", "[127..131) -> (5,0)-(5,4) #4", "[134..138) -> (6,0)-(6,4) #1" - }, oldSpans.Select(s => $"{s.UnmappedSpan} -> {s.Statement.Span} #{s.Statement.Ordinal}")); + }, oldSpans.Select(s => $"{s.UnmappedSpan} -> {s.Statement.Span} #{s.Statement.Id.Ordinal}")); } [Fact] diff --git a/src/Features/Test/EditAndContinue/EditSessionActiveStatementsTests.cs b/src/Features/Test/EditAndContinue/EditSessionActiveStatementsTests.cs index 3c721f2425433..822a8fa24ffe8 100644 --- a/src/Features/Test/EditAndContinue/EditSessionActiveStatementsTests.cs +++ b/src/Features/Test/EditAndContinue/EditSessionActiveStatementsTests.cs @@ -7,22 +7,19 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.EditAndContinue; using Microsoft.CodeAnalysis.Contracts.EditAndContinue; -using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; -using Moq; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; -using System.Text; -using System.IO; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests { @@ -186,7 +183,7 @@ static void Main() // Active Statements - var statements = baseActiveStatementsMap.InstructionMap.Values.OrderBy(v => v.Ordinal).ToArray(); + var statements = baseActiveStatementsMap.InstructionMap.Values.OrderBy(v => v.Id.Ordinal).ToArray(); AssertEx.Equal(new[] { $"0: {document1.FilePath}: (9,14)-(9,35) flags=[LeafFrame, MethodUpToDate] mvid=11111111-1111-1111-1111-111111111111 0x06000001 v1 IL_0001", @@ -344,7 +341,7 @@ static void F2() // Active Statements - var baseActiveStatements = baseActiveStatementMap.InstructionMap.Values.OrderBy(v => v.Ordinal).ToArray(); + var baseActiveStatements = baseActiveStatementMap.InstructionMap.Values.OrderBy(v => v.Id.Ordinal).ToArray(); AssertEx.Equal(new[] { @@ -526,7 +523,7 @@ static void F4() // Active Statements - var baseActiveStatements = baseActiveStatementMap.InstructionMap.Values.OrderBy(v => v.Ordinal).ToArray(); + var baseActiveStatements = baseActiveStatementMap.InstructionMap.Values.OrderBy(v => v.Id.Ordinal).ToArray(); // Note that the spans of AS:2 and AS:3 correspond to the base snapshot (V2). AssertEx.Equal(new[] diff --git a/src/Features/TestUtilities/Diagnostics/MockDiagnosticUpdateSourceRegistrationService.cs b/src/Features/TestUtilities/Diagnostics/MockDiagnosticUpdateSourceRegistrationService.cs deleted file mode 100644 index 73f9dca4b215d..0000000000000 --- a/src/Features/TestUtilities/Diagnostics/MockDiagnosticUpdateSourceRegistrationService.cs +++ /dev/null @@ -1,30 +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.Composition; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics -{ - [Export(typeof(IDiagnosticUpdateSourceRegistrationService))] - [Shared] - [PartNotDiscoverable] - internal class MockDiagnosticUpdateSourceRegistrationService : IDiagnosticUpdateSourceRegistrationService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public MockDiagnosticUpdateSourceRegistrationService() - { - } - - public void Register(IDiagnosticUpdateSource source) - { - // do nothing - } - } -} diff --git a/src/Features/TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs b/src/Features/TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs index 7990005d425ea..8419a40314bd9 100644 --- a/src/Features/TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs +++ b/src/Features/TestUtilities/Diagnostics/TestDiagnosticAnalyzerDriver.cs @@ -8,19 +8,14 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote; -using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.UnitTests.Diagnostics { @@ -37,7 +32,6 @@ public TestDiagnosticAnalyzerDriver(Workspace workspace, bool includeSuppressedD { var mefServices = workspace.Services.SolutionServices.ExportProvider; - Assert.IsType(mefServices.GetExportedValue()); _diagnosticAnalyzerService = Assert.IsType(mefServices.GetExportedValue()); GlobalOptions = mefServices.GetExportedValue(); diff --git a/src/Features/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs b/src/Features/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs index bf804d2cd3592..061e9c607936d 100644 --- a/src/Features/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs +++ b/src/Features/TestUtilities/EditAndContinue/ActiveStatementTestHelpers.cs @@ -93,7 +93,7 @@ public static string Update(string src, string marker) => InsertNewLines(Delete(src, marker), marker); public static string InspectActiveStatement(ActiveStatement statement) - => $"{statement.Ordinal}: {statement.FileSpan} flags=[{statement.Flags}]"; + => $"{statement.Id.Ordinal}: {statement.FileSpan} flags=[{statement.Flags}]"; public static string InspectActiveStatementAndInstruction(ActiveStatement statement) => InspectActiveStatement(statement) + " " + statement.InstructionId.GetDebuggerDisplay(); diff --git a/src/Features/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs b/src/Features/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs index 6aec9fb0ffc27..9685f82743816 100644 --- a/src/Features/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs +++ b/src/Features/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs @@ -23,7 +23,7 @@ internal class ActiveStatementsDescription public readonly ActiveStatementsMap OldStatementsMap; public readonly ImmutableArray NewMappedSpans; public readonly ImmutableArray> NewMappedRegions; - public readonly ImmutableArray OldUnmappedTrackingSpans; + public readonly ImmutableArray OldUnmappedTrackingSpans; private ActiveStatementsDescription() { @@ -61,12 +61,13 @@ public ActiveStatementsDescription(string oldMarkedSource, string newMarkedSourc newMappedRegions.ZeroInit(activeStatementCount); // initialize with deleted spans (they will retain their file path): - foreach (var oldStatement in OldStatements) + for (var i = 0; i < OldStatements.Length; i++) { + var oldStatement = OldStatements[i]; if (oldStatement.Statement != null) { - newMappedSpans[oldStatement.Statement.Ordinal] = new SourceFileSpan(oldStatement.Statement.FilePath, default); - newMappedRegions[oldStatement.Statement.Ordinal] = []; + newMappedSpans[i] = new SourceFileSpan(oldStatement.Statement.FilePath, default); + newMappedRegions[i] = []; } } @@ -86,8 +87,8 @@ public ActiveStatementsDescription(string oldMarkedSource, string newMarkedSourc // edits the source and we get their positions when analyzing the new source. // The EnC analyzer uses old tracking spans as hints to find matching nodes. var newText = newTree.GetText(); - OldUnmappedTrackingSpans = SourceMarkers.GetTrackingSpans(newMarkedSource, activeStatementCount). - SelectAsArray(s => newText.Lines.GetLinePositionSpan(s)); + OldUnmappedTrackingSpans = SourceMarkers.GetTrackingSpans(newMarkedSource). + SelectAsArray(s => new ActiveStatementLineSpan(new ActiveStatementId(s.id), newText.Lines.GetLinePositionSpan(s.span))); } internal static ImmutableArray CreateActiveStatementMapFromMarkers( @@ -117,7 +118,7 @@ internal static ImmutableArray CreateActiveStatementMap var unmappedActiveStatement = new UnmappedActiveStatement( unmappedSpan, new ActiveStatement( - ordinal, + new ActiveStatementId(ordinal), statementFlags, mappedSpan, instructionId: default), @@ -150,7 +151,7 @@ internal static ImmutableArray GetUnmappedActiveStateme sourceIndex++; } - activeStatements.Sort((x, y) => x.Statement.Ordinal.CompareTo(y.Statement.Ordinal)); + activeStatements.Sort((x, y) => x.Statement.Id.Ordinal.CompareTo(y.Statement.Id.Ordinal)); return activeStatements.ToImmutable(); } @@ -167,11 +168,11 @@ internal static ImmutableArray GetActiveStateme new ManagedActiveStatementDebugInfo( new ManagedInstructionId( new ManagedMethodId( - (modules != null) ? modules[statement.Ordinal] : moduleId, + (modules != null) ? modules[statement.Id.Ordinal] : moduleId, new ManagedModuleMethodId( - token: 0x06000000 | (methodRowIds != null ? methodRowIds[statement.Ordinal] : statement.Ordinal + 1), - version: (methodVersions != null) ? methodVersions[statement.Ordinal] : 1)), - ilOffset: (ilOffsets != null) ? ilOffsets[statement.Ordinal] : 0), + token: 0x06000000 | (methodRowIds != null ? methodRowIds[statement.Id.Ordinal] : statement.Id.Ordinal + 1), + version: (methodVersions != null) ? methodVersions[statement.Id.Ordinal] : 1)), + ilOffset: (ilOffsets != null) ? ilOffsets[statement.Id.Ordinal] : 0), documentName: statement.FilePath, sourceSpan: statement.Span.ToSourceSpan(), flags: statement.Flags)); diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index 2f6ad1f92116a..7b64d9003233a 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -60,7 +60,7 @@ private void VerifyDocumentActiveStatementsAndExceptionRegions( ImmutableArray> actualNewExceptionRegions) { // check active statements: - AssertSpansEqual(description.NewMappedSpans, actualNewActiveStatements.OrderBy(x => x.Ordinal).Select(s => s.FileSpan), newTree); + AssertSpansEqual(description.NewMappedSpans, actualNewActiveStatements.OrderBy(x => x.Id.Ordinal).Select(s => s.FileSpan), newTree); var oldRoot = oldTree.GetRoot(); @@ -84,7 +84,7 @@ private void VerifyDocumentActiveStatementsAndExceptionRegions( for (var i = 0; i < actualNewActiveStatements.Length; i++) { var activeStatement = actualNewActiveStatements[i]; - AssertSpansEqual(description.NewMappedRegions[activeStatement.Ordinal], actualNewExceptionRegions[i], newTree); + AssertSpansEqual(description.NewMappedRegions[activeStatement.Id.Ordinal], actualNewExceptionRegions[i], newTree); } } } diff --git a/src/Features/TestUtilities/EditAndContinue/SourceMarkers.cs b/src/Features/TestUtilities/EditAndContinue/SourceMarkers.cs index 5856ad991a36e..0efea7cc9ab11 100644 --- a/src/Features/TestUtilities/EditAndContinue/SourceMarkers.cs +++ b/src/Features/TestUtilities/EditAndContinue/SourceMarkers.cs @@ -85,7 +85,7 @@ internal static string[] Clear(string[] sources) public static IEnumerable<(TextSpan Span, int Id)> GetActiveSpans(string markedSource) => GetSpans(markedSource, tagName: "AS").Select(s => (s.span, s.id.major)); - public static TextSpan[] GetTrackingSpans(string src, int count) + public static (int id, TextSpan span)[] GetTrackingSpans(string src) { var matches = s_trackingStatementPattern.Matches(src); if (matches.Count == 0) @@ -93,20 +93,20 @@ public static TextSpan[] GetTrackingSpans(string src, int count) return []; } - var result = new TextSpan[count]; + var result = new List<(int id, TextSpan span)>(); for (var i = 0; i < matches.Count; i++) { var span = matches[i].Groups["TrackingStatement"]; foreach (var (id, _) in ParseIds(matches[i])) { - result[id] = new TextSpan(span.Index, span.Length); + result.Add((id, new TextSpan(span.Index, span.Length))); } } Contract.ThrowIfTrue(result.Any(span => span == default)); - return result; + return result.ToArray(); } public static ImmutableArray> GetExceptionRegions(string markedSource) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb index 220fea7817f55..206082535f804 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb @@ -9,7 +9,6 @@ Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.ErrorReporting Imports Microsoft.CodeAnalysis.Host.Mef -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Roslyn.Utilities.DocumentationCommentXmlNames @@ -27,6 +26,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers MyBase.New(s_defaultRules) End Sub + Private Shared ReadOnly s_keywordNames As ImmutableArray(Of String) + + Shared Sub New() + Dim keywordsBuilder As New List(Of String) + + For Each keywordKind In SyntaxFacts.GetKeywordKinds() + keywordsBuilder.Add(SyntaxFacts.GetText(keywordKind)) + Next + + s_keywordNames = keywordsBuilder.ToImmutableArray() + End Sub + Friend Overrides ReadOnly Property Language As String Get Return LanguageNames.VisualBasic @@ -270,16 +281,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End If End Sub - Protected Overrides Iterator Function GetKeywordNames() As IEnumerable(Of String) - Yield SyntaxFacts.GetText(SyntaxKind.NothingKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.SharedKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.OverridableKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.TrueKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.FalseKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.MustInheritKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.NotOverridableKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.AsyncKeyword) - Yield SyntaxFacts.GetText(SyntaxKind.AwaitKeyword) + Protected Overrides Function GetKeywordNames() As ImmutableArray(Of String) + Return s_keywordNames End Function Protected Overrides Function GetExistingTopLevelElementNames(parentTrivia As DocumentationCommentTriviaSyntax) As IEnumerable(Of String) diff --git a/src/Features/VisualBasicTest/Diagnostics/Suppression/SuppressionAllCodeTests.vb b/src/Features/VisualBasicTest/Diagnostics/Suppression/SuppressionAllCodeTests.vb index 99cf92d7ad2a7..3d5e494cae508 100644 --- a/src/Features/VisualBasicTest/Diagnostics/Suppression/SuppressionAllCodeTests.vb +++ b/src/Features/VisualBasicTest/Diagnostics/Suppression/SuppressionAllCodeTests.vb @@ -17,9 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Suppre Inherits AbstractSuppressionAllCodeTests Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = FeaturesTestCompositions.Features _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) _ - .AddAssemblies(GetType(DiagnosticService).Assembly) + .AddAssemblies(GetType(DiagnosticAnalyzerService).Assembly) Protected Overrides Function CreateWorkspaceFromFile(definition As String, parseOptions As ParseOptions) As TestWorkspace Return TestWorkspace.CreateVisualBasic(definition, DirectCast(parseOptions, VisualBasicParseOptions), composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) diff --git a/src/Features/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/Features/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index d4ec67748c94b..e6af37ce7b3a3 100644 --- a/src/Features/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/Features/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -123,7 +123,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Dim baseActiveStatements = AsyncLazy.Create(If(activeStatementMap, ActiveStatementsMap.Empty)) Dim capabilities = AsyncLazy.Create(EditAndContinueTestHelpers.Net5RuntimeCapabilities) - Return Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray(Of LinePositionSpan).Empty, capabilities, CancellationToken.None) + Return Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray(Of ActiveStatementLineSpan).Empty, capabilities, CancellationToken.None) End Function #End Region @@ -485,7 +485,7 @@ End Class { KeyValuePairUtil.Create(newDocument.FilePath, ImmutableArray.Create( New ActiveStatement( - ordinal:=0, + New ActiveStatementId(0), ActiveStatementFlags.LeafFrame, New SourceFileSpan(newDocument.FilePath, oldStatementSpan), instructionId:=Nothing))) diff --git a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj index 49a4b6546224f..d62e1c2961bef 100644 --- a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj +++ b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj @@ -2,7 +2,8 @@ net472 - true + + true false diff --git a/src/Setup/DevDivVsix/ServiceHubConfig/Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config.csproj b/src/Setup/DevDivVsix/ServiceHubConfig/Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config.csproj deleted file mode 100644 index 51fa8dabdbd53..0000000000000 --- a/src/Setup/DevDivVsix/ServiceHubConfig/Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config.csproj +++ /dev/null @@ -1,62 +0,0 @@ - - - - - net472 - - - Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config.vsix - Microsoft.CodeAnalysis.LanguageServices - - - - ..\..\..\IDE\$(CommonExtensionInstallationRoot)\$(LanguageServicesExtensionInstallationFolder)\ - desktop - - - - - <_SwrFilePath>$(IntermediateOutputPath)Roslyn.VisualStudio.Setup.ServiceHub.Desktop.Config.swr - - - - - - - - <_ServiceHubConfigFiles Include="@(ServiceHubService)" FileSuffix="64" /> - <_ServiceHubConfigFiles Include="@(ServiceHubService)" FileSuffix="64S" /> - <_FileEntries Include='file source="$(IntermediateOutputPath)%(_ServiceHubConfigFiles.Identity)%(_ServiceHubConfigFiles.FileSuffix).servicehub.service.json"'/> - - - - <_Lines> - - - - - - - - - - - - - - diff --git a/src/Tools/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotAnalyzer.cs b/src/Tools/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs similarity index 90% rename from src/Tools/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotAnalyzer.cs rename to src/Tools/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs index c5e36413a3e02..0314ebdb89ff3 100644 --- a/src/Tools/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotAnalyzer.cs +++ b/src/Tools/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs @@ -5,12 +5,11 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot; -internal interface IExternalCopilotCodeAnalysisService : ILanguageService +internal interface IExternalCSharpCopilotCodeAnalysisService { // mirror the ICopilotCodeAnalysisService interface Task IsAvailableAsync(CancellationToken cancellation); diff --git a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs index 140b63cf0eaf2..245d17f7c20ff 100644 --- a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs +++ b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Collections.Immutable; using System.Runtime.CompilerServices; using System.Threading; @@ -24,9 +25,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer; /// Additionally, it performs all the option checks and Copilot service availability checks /// to determine if we should skip analysis or not. /// -internal abstract class AbstractCopilotCodeAnalysisService( - Lazy lazyExternalCopilotService, - IDiagnosticsRefresher diagnosticsRefresher) : ICopilotCodeAnalysisService +internal abstract class AbstractCopilotCodeAnalysisService(IDiagnosticsRefresher diagnosticsRefresher) : ICopilotCodeAnalysisService { // The _diagnosticsCache is a cache for computed diagnostics via `AnalyzeDocumentAsync`. // Each document maps to a dictionary, which in tern maps a prompt title to a list of existing Diagnostics and a boolean flag. @@ -36,18 +35,23 @@ internal abstract class AbstractCopilotCodeAnalysisService( private readonly ConditionalWeakTable Diagnostics, bool IsCompleteResult)>> _diagnosticsCache = new(); public abstract Task IsRefineOptionEnabledAsync(); - public abstract Task IsCodeAnalysisOptionEnabledAsync(); + protected abstract Task IsAvailableCoreAsync(CancellationToken cancellationToken); + protected abstract Task> GetAvailablePromptTitlesCoreAsync(Document document, CancellationToken cancellationToken); + protected abstract Task> AnalyzeDocumentCoreAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken); + protected abstract Task> GetCachedDiagnosticsCoreAsync(Document document, string promptTitle, CancellationToken cancellationToken); + protected abstract Task StartRefinementSessionCoreAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken); + public Task IsAvailableAsync(CancellationToken cancellationToken) - => lazyExternalCopilotService.Value.IsAvailableAsync(cancellationToken); + => IsAvailableCoreAsync(cancellationToken); public async Task> GetAvailablePromptTitlesAsync(Document document, CancellationToken cancellationToken) { if (!await IsCodeAnalysisOptionEnabledAsync().ConfigureAwait(false)) return []; - return await lazyExternalCopilotService.Value.GetAvailablePromptTitlesAsync(document, cancellationToken).ConfigureAwait(false); + return await GetAvailablePromptTitlesCoreAsync(document, cancellationToken).ConfigureAwait(false); } private async Task ShouldSkipAnalysisAsync(Document document, CancellationToken cancellationToken) @@ -73,7 +77,7 @@ public async Task AnalyzeDocumentAsync(Document document, TextSpan? span, string return; var isFullDocumentAnalysis = !span.HasValue; - var diagnostics = await lazyExternalCopilotService.Value.AnalyzeDocumentAsync(document, span, promptTitle, cancellationToken).ConfigureAwait(false); + var diagnostics = await AnalyzeDocumentCoreAsync(document, span, promptTitle, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); @@ -116,7 +120,7 @@ private void CacheAndRefreshDiagnosticsIfNeeded(Document document, string prompt diagnosticsRefresher.RequestWorkspaceRefresh(); } - public async Task> GetCachedDocumentDiagnosticsAsync(Document document, ImmutableArray promptTitles, CancellationToken cancellationToken) + public async Task> GetCachedDocumentDiagnosticsAsync(Document document, TextSpan? span, ImmutableArray promptTitles, CancellationToken cancellationToken) { if (await ShouldSkipAnalysisAsync(document, cancellationToken).ConfigureAwait(false)) return []; @@ -138,18 +142,26 @@ public async Task> GetCachedDocumentDiagnosticsAsync( } else { - var cachedDiagnostics = await lazyExternalCopilotService.Value.GetCachedDiagnosticsAsync(document, promptTitle, cancellationToken).ConfigureAwait(false); + var cachedDiagnostics = await GetCachedDiagnosticsCoreAsync(document, promptTitle, cancellationToken).ConfigureAwait(false); diagnostics.AddRange(cachedDiagnostics); CacheAndRefreshDiagnosticsIfNeeded(document, promptTitle, cachedDiagnostics, isCompleteResult: false); } } + if (span.HasValue) + return await GetDiagnosticsIntersectWithSpanAsync(document, diagnostics, span.Value, cancellationToken).ConfigureAwait(false); + return diagnostics.ToImmutable(); } + protected virtual Task> GetDiagnosticsIntersectWithSpanAsync(Document document, IReadOnlyList diagnostics, TextSpan span, CancellationToken cancellationToken) + { + return Task.FromResult(diagnostics.WhereAsArray((diagnostic, _) => diagnostic.Location.SourceSpan.IntersectsWith(span), state: (object)null)); + } + public async Task StartRefinementSessionAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken) { if (await IsRefineOptionEnabledAsync().ConfigureAwait(false)) - await lazyExternalCopilotService.Value.StartRefinementSessionAsync(oldDocument, newDocument, primaryDiagnostic, cancellationToken).ConfigureAwait(false); + await StartRefinementSessionCoreAsync(oldDocument, newDocument, primaryDiagnostic, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs index 987c73f48c0ca..adb3f8264ab57 100644 --- a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs +++ b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.ReflectionWrapper.cs @@ -22,8 +22,8 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer.CSharp internal sealed partial class CSharpCopilotCodeAnalysisService { - // A temporary helper to get access to the implementation of IExternalCopilotCodeAnalysisService, until it can be MEF exported. - private sealed class ReflectionWrapper : IExternalCopilotCodeAnalysisService + // A temporary helper to get access to the implementation of IExternalCSharpCopilotCodeAnalysisService, until it can be MEF exported. + private sealed class ReflectionWrapper : IExternalCSharpCopilotCodeAnalysisService { private const string CopilotRoslynDllName = "Microsoft.VisualStudio.Copilot.Roslyn, Version=0.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; private const string InternalCSharpCopilotAnalyzerTypeFullName = "Microsoft.VisualStudio.Copilot.Roslyn.Analyzer.InternalCSharpCopilotAnalyzer"; diff --git a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs index 3d930e1f562bf..8150e9d3880ce 100644 --- a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs +++ b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs @@ -3,41 +3,89 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.ServiceBroker; namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer.CSharp; -internal sealed partial class CSharpCopilotCodeAnalysisService( - Lazy lazyExternalCopilotService, - IDiagnosticsRefresher diagnosticsRefresher, - VisualStudioCopilotOptionService copilotOptionService) : AbstractCopilotCodeAnalysisService(lazyExternalCopilotService, diagnosticsRefresher) +[ExportLanguageService(typeof(ICopilotCodeAnalysisService), LanguageNames.CSharp), Shared] +internal sealed partial class CSharpCopilotCodeAnalysisService : AbstractCopilotCodeAnalysisService { private const string CopilotRefineOptionName = "EnableCSharpRefineQuickActionSuggestion"; private const string CopilotCodeAnalysisOptionName = "EnableCSharpCodeAnalysis"; - public static CSharpCopilotCodeAnalysisService Create( - HostLanguageServices languageServices, - IDiagnosticsRefresher diagnosticsRefresher, + private readonly Lazy _lazyExternalCopilotService; + private readonly VisualStudioCopilotOptionService _copilotOptionService; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpCopilotCodeAnalysisService( + [Import(AllowDefault = true)] IExternalCSharpCopilotCodeAnalysisService? externalCopilotService, VisualStudioCopilotOptionService copilotOptionService, + IDiagnosticsRefresher diagnosticsRefresher, SVsServiceProvider serviceProvider, - IVsService brokeredServiceContainer) + IVsService brokeredServiceContainer + ) : base(diagnosticsRefresher) { - var lazyExternalCopilotService = new Lazy(GetExternalService, LazyThreadSafetyMode.PublicationOnly); - return new CSharpCopilotCodeAnalysisService(lazyExternalCopilotService, diagnosticsRefresher, copilotOptionService); + _lazyExternalCopilotService = new Lazy(GetExternalService, LazyThreadSafetyMode.PublicationOnly); + _copilotOptionService = copilotOptionService; - IExternalCopilotCodeAnalysisService GetExternalService() - => languageServices.GetService() ?? new ReflectionWrapper(serviceProvider, brokeredServiceContainer); + IExternalCSharpCopilotCodeAnalysisService GetExternalService() + => externalCopilotService ?? new ReflectionWrapper(serviceProvider, brokeredServiceContainer); } public override Task IsRefineOptionEnabledAsync() - => copilotOptionService.IsCopilotOptionEnabledAsync(CopilotRefineOptionName); + => _copilotOptionService.IsCopilotOptionEnabledAsync(CopilotRefineOptionName); public override Task IsCodeAnalysisOptionEnabledAsync() - => copilotOptionService.IsCopilotOptionEnabledAsync(CopilotCodeAnalysisOptionName); + => _copilotOptionService.IsCopilotOptionEnabledAsync(CopilotCodeAnalysisOptionName); + + protected override Task> AnalyzeDocumentCoreAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken) + => _lazyExternalCopilotService.Value.AnalyzeDocumentAsync(document, span, promptTitle, cancellationToken); + + protected override Task> GetAvailablePromptTitlesCoreAsync(Document document, CancellationToken cancellationToken) + => _lazyExternalCopilotService.Value.GetAvailablePromptTitlesAsync(document, cancellationToken); + + protected override Task> GetCachedDiagnosticsCoreAsync(Document document, string promptTitle, CancellationToken cancellationToken) + => _lazyExternalCopilotService.Value.GetCachedDiagnosticsAsync(document, promptTitle, cancellationToken); + + protected override Task IsAvailableCoreAsync(CancellationToken cancellationToken) + => _lazyExternalCopilotService.Value.IsAvailableAsync(cancellationToken); + + protected override Task StartRefinementSessionCoreAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken) + => _lazyExternalCopilotService.Value.StartRefinementSessionAsync(oldDocument, newDocument, primaryDiagnostic, cancellationToken); + + protected override async Task> GetDiagnosticsIntersectWithSpanAsync( + Document document, IReadOnlyList diagnostics, TextSpan span, CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var filteredDiagnostics); + + // The location of Copilot diagnostics is on the method identifier, we'd like to expand the range to include them + // if any part of the method intersects with the given span. + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); + + foreach (var diagnostic in diagnostics) + { + var containingMethod = syntaxFacts.GetContainingMethodDeclaration(root, diagnostic.Location.SourceSpan.Start, useFullSpan: false); + if (containingMethod?.Span.IntersectsWith(span) is true) + filteredDiagnostics.Add(diagnostic); + } + + return filteredDiagnostics.ToImmutable(); + } } diff --git a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisServiceFactory.cs b/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisServiceFactory.cs deleted file mode 100644 index 8a9e3bc248540..0000000000000 --- a/src/Tools/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisServiceFactory.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 System; -using System.Composition; -using Microsoft.CodeAnalysis.Copilot; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.ServiceBroker; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer.CSharp; - -[ExportLanguageServiceFactory(typeof(ICopilotCodeAnalysisService), LanguageNames.CSharp), Shared] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed partial class CSharpCopilotCodeAnalysisServiceFactory( - IDiagnosticsRefresher diagnosticsRefresher, - VisualStudioCopilotOptionService copilotOptionService, - SVsServiceProvider serviceProvider, - IVsService brokeredServiceContainer) : ILanguageServiceFactory -{ - public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - => CSharpCopilotCodeAnalysisService.Create(languageServices, diagnosticsRefresher, copilotOptionService, serviceProvider, brokeredServiceContainer); -} diff --git a/src/Tools/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/Tools/ExternalAccess/Copilot/InternalAPI.Unshipped.txt index c5a3d62f3596d..f2eec4444e3eb 100644 --- a/src/Tools/ExternalAccess/Copilot/InternalAPI.Unshipped.txt +++ b/src/Tools/ExternalAccess/Copilot/InternalAPI.Unshipped.txt @@ -4,12 +4,12 @@ Microsoft.CodeAnalysis.ExternalAccess.Copilot.CodeMapper.ICSharpCopilotMapCodeSe Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper.Equals(Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper? other) -> bool Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotUtilities -Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCopilotCodeAnalysisService -Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCopilotCodeAnalysisService.AnalyzeDocumentAsync(Microsoft.CodeAnalysis.Document! document, Microsoft.CodeAnalysis.Text.TextSpan? span, string! promptTitle, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! -Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCopilotCodeAnalysisService.GetAvailablePromptTitlesAsync(Microsoft.CodeAnalysis.Document! document, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! -Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCopilotCodeAnalysisService.GetCachedDiagnosticsAsync(Microsoft.CodeAnalysis.Document! document, string! promptTitle, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! -Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCopilotCodeAnalysisService.IsAvailableAsync(System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.Task! -Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCopilotCodeAnalysisService.StartRefinementSessionAsync(Microsoft.CodeAnalysis.Document! oldDocument, Microsoft.CodeAnalysis.Document! newDocument, Microsoft.CodeAnalysis.Diagnostic? primaryDiagnostic, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.AnalyzeDocumentAsync(Microsoft.CodeAnalysis.Document! document, Microsoft.CodeAnalysis.Text.TextSpan? span, string! promptTitle, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.GetAvailablePromptTitlesAsync(Microsoft.CodeAnalysis.Document! document, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.GetCachedDiagnosticsAsync(Microsoft.CodeAnalysis.Document! document, string! promptTitle, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.IsAvailableAsync(System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.StartRefinementSessionAsync(Microsoft.CodeAnalysis.Document! oldDocument, Microsoft.CodeAnalysis.Document! newDocument, Microsoft.CodeAnalysis.Diagnostic? primaryDiagnostic, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! override Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper.Equals(object? obj) -> bool override Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper.GetHashCode() -> int static Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper.Create(System.Collections.Immutable.ImmutableArray values) -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotChecksumWrapper! diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDiagnosticAnalyzerService.cs b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDiagnosticAnalyzerService.cs index f77fd03c4b738..b4fef38d66eef 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDiagnosticAnalyzerService.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDiagnosticAnalyzerService.cs @@ -26,8 +26,6 @@ public FSharpDiagnosticAnalyzerService(Microsoft.CodeAnalysis.Diagnostics.IDiagn } public void Reanalyze(Workspace workspace, IEnumerable projectIds = null, IEnumerable documentIds = null, bool highPriority = false) - { - _delegatee.Reanalyze(workspace, projectIds, documentIds, highPriority); - } + => _delegatee.RequestDiagnosticRefresh(); } } diff --git a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs b/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs index b27bd3359fed1..03b807c66d944 100644 --- a/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs +++ b/src/Tools/ExternalAccess/Razor/RazorTestAnalyzerLoader.cs @@ -4,32 +4,25 @@ using System; using System.Composition; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.SolutionCrawler; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; [Export(typeof(RazorTestAnalyzerLoader)), Shared] internal class RazorTestAnalyzerLoader { - private readonly IDiagnosticAnalyzerService _analyzerService; - private readonly DiagnosticService _diagnosticService; - [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RazorTestAnalyzerLoader(IDiagnosticAnalyzerService analyzerService, IDiagnosticService diagnosticService) + public RazorTestAnalyzerLoader() { - _analyzerService = analyzerService; - _diagnosticService = (DiagnosticService)diagnosticService; } #pragma warning disable IDE0060 // Remove unused parameter +#pragma warning disable CA1822 // Mark members as static public void InitializeDiagnosticsServices(Workspace workspace) +#pragma warning restore CA1822 // Mark members as static #pragma warning restore IDE0060 // Remove unused parameter { - _diagnosticService.Register((IDiagnosticUpdateSource)_analyzerService); } public static IAnalyzerAssemblyLoader CreateAnalyzerAssemblyLoader() diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/Contracts/IVisualDiagnosticsLanguageService.cs b/src/Tools/ExternalAccess/VisualDiagnostics/Contracts/IVisualDiagnosticsLanguageService.cs new file mode 100644 index 0000000000000..fa0bfc3b929b4 --- /dev/null +++ b/src/Tools/ExternalAccess/VisualDiagnostics/Contracts/IVisualDiagnosticsLanguageService.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.ServiceHub.Framework; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts +{ + /// + /// Workspace service responsible for starting a Visual Diagnostic session on the LSP server + /// + internal interface IVisualDiagnosticsLanguageService : IWorkspaceService, IDisposable + { + /// + /// Initialize the diagnostic host + /// + /// Service broker + /// Cancellation token + /// + Task InitializeAsync(IServiceBroker serviceBroker, CancellationToken token); + } +} diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs b/src/Tools/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs new file mode 100644 index 0000000000000..ce9a367059a7d --- /dev/null +++ b/src/Tools/ExternalAccess/VisualDiagnostics/Internal/VisualDiagnosticsServiceFactory.cs @@ -0,0 +1,89 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.BrokeredServices; +using Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.ServiceHub.Framework; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics; + +/// +/// LSP Service responsible for loading IVisualDiagnosticsLanguageService workspace service and delegate the broker service to the workspace service, +/// and handling MAUI XAML/C#/CSS/Razor Hot Reload support +/// +[Export(typeof(IOnServiceBrokerInitialized))] +[ExportCSharpVisualBasicLspServiceFactory(typeof(OnInitializedService)), Shared] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +[method: ImportingConstructor] +internal sealed class VisualDiagnosticsServiceFactory( + LspWorkspaceRegistrationService lspWorkspaceRegistrationService) : ILspServiceFactory, IOnServiceBrokerInitialized +{ + private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; + private readonly Lazy _OnInitializedService = new Lazy(() => new OnInitializedService(lspWorkspaceRegistrationService)); + + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + return _OnInitializedService.Value; + } + + public void OnServiceBrokerInitialized(IServiceBroker serviceBroker) + { + _OnInitializedService.Value.OnServiceBrokerInitialized(serviceBroker); + } + + private class OnInitializedService : ILspService, IOnInitialized, IOnServiceBrokerInitialized, IDisposable + { + private readonly LspWorkspaceRegistrationService _lspWorkspaceRegistrationService; + private IVisualDiagnosticsLanguageService? _visualDiagnosticsLanguageService; + private CancellationToken _cancellationToken; + private static readonly TaskCompletionSource _taskCompletionSource = new TaskCompletionSource(); + + public OnInitializedService(LspWorkspaceRegistrationService lspWorkspaceRegistrationService) + { + _lspWorkspaceRegistrationService = lspWorkspaceRegistrationService; + } + + public void Dispose() + { + (_visualDiagnosticsLanguageService as IDisposable)?.Dispose(); + } + + public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) + { + _cancellationToken = cancellationToken; + _taskCompletionSource.TrySetResult(true); + return Task.CompletedTask; + } + + public void OnServiceBrokerInitialized(IServiceBroker serviceBroker) + { + _taskCompletionSource.Task.ContinueWith((initialized) => OnInitializeVisualDiagnosticsLanguageServiceAsync(serviceBroker), TaskScheduler.Default); + } + + private async Task OnInitializeVisualDiagnosticsLanguageServiceAsync(IServiceBroker serviceBroker) + { + // initialize VisualDiagnosticsLanguageService + Workspace workspace = _lspWorkspaceRegistrationService.GetAllRegistrations().First(w => w.Kind == WorkspaceKind.Host); + Contract.ThrowIfFalse(workspace != null, "We should always have a host workspace."); + + IVisualDiagnosticsLanguageService? visualDiagnosticsLanguageService = workspace.Services.GetService(); + + if (visualDiagnosticsLanguageService != null) + { + await visualDiagnosticsLanguageService.InitializeAsync(serviceBroker, _cancellationToken).ConfigureAwait(false); + _visualDiagnosticsLanguageService = visualDiagnosticsLanguageService; + } + } + } +} diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/InternalAPI.Shipped.txt b/src/Tools/ExternalAccess/VisualDiagnostics/InternalAPI.Shipped.txt new file mode 100644 index 0000000000000..7dc5c58110bfa --- /dev/null +++ b/src/Tools/ExternalAccess/VisualDiagnostics/InternalAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/InternalAPI.Unshipped.txt b/src/Tools/ExternalAccess/VisualDiagnostics/InternalAPI.Unshipped.txt new file mode 100644 index 0000000000000..504106533fe6b --- /dev/null +++ b/src/Tools/ExternalAccess/VisualDiagnostics/InternalAPI.Unshipped.txt @@ -0,0 +1,18 @@ +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsLanguageService +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsLanguageService.HandleDiagnosticSessionStartAsync(Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.ProcessInfo info, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsLanguageService.HandleDiagnosticSessionStopAsync(Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.ProcessInfo info, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsLanguageService.InitializeAsync(Microsoft.ServiceHub.Framework.IServiceBroker! serviceBroker, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsLanguageService.RequestDataBridgeConnectionAsync(string! connectionId, System.Threading.CancellationToken token) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsServiceBroker +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Contracts.IVisualDiagnosticsServiceBroker.GetServiceBrokerAsync() -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Internal.VisualDiagnosticsServiceBroker +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Internal.VisualDiagnosticsServiceBroker.NotifyServiceBrokerInitialized.get -> Microsoft.CodeAnalysis.BrokeredServices.IOnServiceBrokerInitialized? +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Internal.VisualDiagnosticsServiceBroker.NotifyServiceBrokerInitialized.set -> void +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Internal.VisualDiagnosticsServiceBroker.OnServiceBrokerInitialized(Microsoft.ServiceHub.Framework.IServiceBroker! serviceBroker) -> void +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.Internal.VisualDiagnosticsServiceBroker.VisualDiagnosticsServiceBroker() -> void +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.RunningProcessEntry +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.RunningProcessEntry.RunningProcessEntry() -> void +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.VisualDiagnosticsServiceFactory +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.VisualDiagnosticsServiceFactory.CreateILspService(Microsoft.CodeAnalysis.LanguageServer.LspServices! lspServices, Microsoft.CodeAnalysis.LanguageServer.WellKnownLspServerKinds serverKind) -> Microsoft.CodeAnalysis.LanguageServer.ILspService! +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.VisualDiagnosticsServiceFactory.OnServiceBrokerInitialized(Microsoft.ServiceHub.Framework.IServiceBroker! serviceBroker) -> void +Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.VisualDiagnosticsServiceFactory.VisualDiagnosticsServiceFactory(Microsoft.CodeAnalysis.LanguageServer.LspWorkspaceRegistrationService! lspWorkspaceRegistrationService) -> void \ No newline at end of file diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.csproj b/src/Tools/ExternalAccess/VisualDiagnostics/Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.csproj new file mode 100644 index 0000000000000..ada2581f782b5 --- /dev/null +++ b/src/Tools/ExternalAccess/VisualDiagnostics/Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics.csproj @@ -0,0 +1,35 @@ + + + + + Library + Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics + netstandard2.0 + + + true + Microsoft.CodeAnalysis.ExternalAccess.VisualDiagnostics + + A supporting package for Visual Studio Microsoft.VisualStudio.DesignTools.CodeAnalysis.Diagnostics: + https://devdiv.visualstudio.com/DevDiv/_git/VS?path=/src/Xaml/Diagnostics/Source/CodeAnalysisDiagnostics + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/PublicAPI.Shipped.txt b/src/Tools/ExternalAccess/VisualDiagnostics/PublicAPI.Shipped.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Tools/ExternalAccess/VisualDiagnostics/PublicAPI.Unshipped.txt b/src/Tools/ExternalAccess/VisualDiagnostics/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs b/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs new file mode 100644 index 0000000000000..1bfd842325f35 --- /dev/null +++ b/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs @@ -0,0 +1,490 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Security.Cryptography; +using System.Text.RegularExpressions; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.CodeAnalysis.CSharp; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Tools; + +internal readonly record struct ApiPattern( + SymbolKindFlags SymbolKinds, + Regex MetadataNamePattern, + bool IsIncluded); + +[Flags] +internal enum SymbolKindFlags +{ + None = 0, + NamedType = 1, + Method = 1 << 1, + Field = 1 << 3, +} + +/// +/// The task transforms given assemblies by changing the visibility of members defined in these assemblies +/// based on filter patterns specified in the corresponding . +/// are text files whose file names (without extension) match the file names of . +/// Each API set specifies a list of patterns that define which members should be included or excluded from the output assembly. +/// All excluded members are made internal or private. +/// +public sealed class GenerateFilteredReferenceAssembliesTask : Task +{ + private static readonly Regex s_lineSyntax = new(""" + ^ + \s* + (?[+|-]?) + ((?[A-Za-z]+):)? + (?[^#]*) + ([#].*)? + $ + """, RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace); + + [Required] + public ITaskItem[] ApiSets { get; private set; } = null!; + + [Required] + public ITaskItem[] References { get; private set; } = null!; + + [Required] + public string OutputDir { get; private set; } = null!; + + public override bool Execute() + { + try + { + ExecuteImpl(); + } + catch (Exception e) + { + Log.LogError($"GenerateFilteredReferenceAssembliesTask failed with exception:{Environment.NewLine}{e}"); + } + + return !Log.HasLoggedErrors; + } + + private void ExecuteImpl() + { + ExecuteImpl(ApiSets.Select(item => (item.ItemSpec, (IReadOnlyList)File.ReadAllLines(item.ItemSpec)))); + } + + internal void ExecuteImpl(IEnumerable<(string apiSpecPath, IReadOnlyList lines)> apiSets) + { + var referencesByName = References.ToDictionary(r => Path.GetFileNameWithoutExtension(r.ItemSpec), r => r.ItemSpec); + + foreach (var (specPath, filters) in apiSets) + { + var assemblyName = Path.GetFileNameWithoutExtension(specPath); + if (!referencesByName.TryGetValue(assemblyName, out var originalReferencePath)) + { + Log.LogWarning($"Assembly '{assemblyName}' not found among project references"); + continue; + } + + var filteredReferencePath = Path.Combine(OutputDir, assemblyName + ".dll"); + var errors = new List<(string message, int line)>(); + var patterns = new List(); + ParseApiPatterns(filters, errors, patterns); + + foreach (var (message, line) in errors) + { + Log.LogWarning($"Invalid API pattern at {specPath} line {line}: {message}"); + } + + var peImageBuffer = File.ReadAllBytes(originalReferencePath); + Rewrite(peImageBuffer, patterns.ToImmutableArray()); + + try + { + File.WriteAllBytes(filteredReferencePath, peImageBuffer); + } + catch when (File.Exists(filteredReferencePath)) + { + // Another instance of the task might already be writing the content. + Log.LogMessage($"Output file '{filteredReferencePath}' already exists."); + } + } + } + + internal static void ParseApiPatterns(IReadOnlyList lines, List<(string message, int line)> errors, List patterns) + { + for (var i = 0; i < lines.Count; i++) + { + var line = lines[i]; + + var match = s_lineSyntax.Match(line); + if (!match.Success) + { + errors.Add(("unable to parse", i + 1)); + continue; + } + + var inclusion = match.Groups["Inclusion"].Value; + var kinds = match.Groups["Kinds"].Value; + var metadataName = match.Groups["MetadataName"].Value; + + var hasSymbolKindError = false; + var symbolKinds = SymbolKindFlags.None; + foreach (var kind in kinds) + { + symbolKinds |= kind switch + { + 'F' => SymbolKindFlags.Field, + 'M' => SymbolKindFlags.Method, + 'T' => SymbolKindFlags.NamedType, + _ => Unexpected() + }; + + SymbolKindFlags Unexpected() + { + hasSymbolKindError = true; + errors.Add(($"unexpected symbol kind: '{kind}'", i + 1)); + return SymbolKindFlags.None; + } + } + + if (hasSymbolKindError) + { + continue; + } + + if (symbolKinds == SymbolKindFlags.None) + { + symbolKinds = SymbolKindFlags.NamedType; + } + + if (metadataName is "") + { + if (inclusion is not "" || kinds is not "") + { + errors.Add(("expected metadata name", i + 1)); + } + + continue; + } + + patterns.Add(new() + { + SymbolKinds = symbolKinds, + MetadataNamePattern = ParseApiPattern(metadataName), + IsIncluded = inclusion is not ['-'] + }); + } + } + + /// + /// Interprets `*` as `.*` and escapes the rest of regex-special characters. + /// + internal static Regex ParseApiPattern(string pattern) + => new("^" + string.Join(".*", pattern.Trim().Split('*').Select(Regex.Escape)) + "$", + RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); + + internal static void GetAllMembers( + Compilation compilation, + List types, + List methods, + List fields) + { + Recurse(compilation.GlobalNamespace.GetMembers()); + + void Recurse(IEnumerable members) + { + foreach (var member in members) + { + switch (member) + { + case INamedTypeSymbol type: + if (type.MetadataToken != 0) + { + types.Add(type); + Recurse(type.GetMembers()); + } + break; + + case IMethodSymbol method: + if (method.MetadataToken != 0) + { + methods.Add(method); + } + break; + + case IFieldSymbol field: + if (field.MetadataToken != 0) + { + fields.Add(field); + } + break; + + case INamespaceSymbol ns: + Recurse(ns.GetMembers()); + break; + } + } + } + } + + private static bool IsIncluded(ISymbol symbol, ImmutableArray patterns) + { + var id = symbol.GetDocumentationCommentId(); + Debug.Assert(id is [_, ':', ..]); + id = id[2..]; + + var kind = GetKindFlags(symbol); + + // Type symbols areconsidered excluded by default. + // Member symbols are included by default since their type limits the effective visibility. + var isIncluded = symbol is not INamedTypeSymbol; + + foreach (var pattern in patterns) + { + if ((pattern.SymbolKinds & kind) == kind && pattern.MetadataNamePattern.IsMatch(id)) + { + isIncluded = pattern.IsIncluded; + } + } + + return isIncluded; + } + + private static SymbolKindFlags GetKindFlags(ISymbol symbol) + => symbol.Kind switch + { + SymbolKind.Field => SymbolKindFlags.Field, + SymbolKind.Method => SymbolKindFlags.Method, + SymbolKind.NamedType => SymbolKindFlags.NamedType, + _ => throw ExceptionUtilities.UnexpectedValue(symbol.Kind) + }; + + internal static unsafe void Rewrite(byte[] peImage, ImmutableArray patterns) + { + // Include all APIs if no patterns are specified. + if (patterns.IsEmpty) + { + return; + } + + using var readableStream = new MemoryStream(peImage, writable: false); + var metadataRef = MetadataReference.CreateFromStream(readableStream); + var compilation = CSharpCompilation.Create("Metadata", references: [metadataRef]); + + // Collect all member definitions that have visibility flags: + var types = new List(); + var methods = new List(); + var fields = new List(); + GetAllMembers(compilation, types, methods, fields); + + // Update visibility flags: + using var writableStream = new MemoryStream(peImage, writable: true); + using var peReader = new PEReader(writableStream); + using var writer = new BinaryWriter(writableStream); + + var headers = peReader.PEHeaders; + Debug.Assert(headers.PEHeader != null); + + var metadataReader = peReader.GetMetadataReader(); + var metadataOffset = peReader.PEHeaders.MetadataStartOffset; + + UpdateTypeDefinitions( + writer, + metadataReader, + patterns, + types.OrderBy(t => t.MetadataToken).ToImmutableArray(), + metadataOffset); + + UpdateMethodDefinitions( + writer, + metadataReader, + patterns, + methods.OrderBy(t => t.MetadataToken).ToImmutableArray(), + metadataOffset); + + UpdateFieldDefinitions( + writer, + metadataReader, + patterns, + fields.OrderBy(t => t.MetadataToken).ToImmutableArray(), + metadataOffset); + + // unsign: + if (headers.PEHeader.CertificateTableDirectory.Size > 0) + { + var certificateTableDirectoryOffset = (headers.PEHeader.Magic == PEMagic.PE32Plus) ? 144 : 128; + writableStream.Position = peReader.PEHeaders.PEHeaderStartOffset + certificateTableDirectoryOffset; + writer.Write((long)0); + } + + writer.Flush(); + + // update mvid: + var moduleDef = metadataReader.GetModuleDefinition(); + var mvidOffset = metadataOffset + metadataReader.GetHeapMetadataOffset(HeapIndex.Guid) + (MetadataTokens.GetHeapOffset(moduleDef.Mvid) - 1) * sizeof(Guid); +#if DEBUG + writableStream.Position = mvidOffset; + Debug.Assert(metadataReader.GetGuid(moduleDef.Mvid) == ReadGuid(writableStream)); +#endif + var newMvid = CreateMvid(writableStream); + writableStream.Position = mvidOffset; + WriteGuid(writer, newMvid); + + writer.Flush(); + } + + private static unsafe TSymbol? GetSymbolWithToken(ImmutableArray symbols, ref int symbolIndex, EntityHandle handle) where TSymbol : class, ISymbol + // If the current definition does not have corresponding symbol, + // we couldn't decode the symbol from metadata. Treat such definition as excluded. + => (symbolIndex < symbols.Length && symbols[symbolIndex].MetadataToken == MetadataTokens.GetToken(handle)) ? symbols[symbolIndex++] : null; + + private static unsafe void UpdateTypeDefinitions(BinaryWriter writer, MetadataReader metadataReader, ImmutableArray patterns, ImmutableArray symbols, int metadataOffset) + { + var tableOffset = metadataOffset + metadataReader.GetTableMetadataOffset(TableIndex.TypeDef); + var tableRowSize = metadataReader.GetTableRowSize(TableIndex.TypeDef); + var symbolIndex = 0; + + foreach (var handle in metadataReader.TypeDefinitions) + { + var symbol = GetSymbolWithToken(symbols, ref symbolIndex, handle); + if (symbol == null || !IsIncluded(symbol, patterns)) + { + var typeDef = metadataReader.GetTypeDefinition(handle); + + // reduce visibility so that the type is not visible outside the assembly: + var oldVisibility = typeDef.Attributes & TypeAttributes.VisibilityMask; + var newVisibility = oldVisibility switch + { + TypeAttributes.Public => TypeAttributes.NotPublic, + TypeAttributes.NestedPublic or TypeAttributes.NestedFamily or TypeAttributes.NestedFamORAssem => TypeAttributes.NestedAssembly, + _ => oldVisibility + }; + + if (oldVisibility == newVisibility) + { + continue; + } + + // Type attributes are store as the first field of the row and are 4B + var offset = tableOffset + (MetadataTokens.GetRowNumber(handle) - 1) * tableRowSize + 0; +#if DEBUG + writer.BaseStream.Position = offset; + Debug.Assert((TypeAttributes)ReadUInt32(writer.BaseStream) == typeDef.Attributes); +#endif + writer.BaseStream.Position = offset; + Debug.Assert(BitConverter.IsLittleEndian); + writer.Write((uint)(typeDef.Attributes & ~TypeAttributes.VisibilityMask | newVisibility)); + } + } + } + + private static unsafe void UpdateMethodDefinitions(BinaryWriter writer, MetadataReader metadataReader, ImmutableArray patterns, ImmutableArray symbols, int metadataOffset) + { + var tableOffset = metadataOffset + metadataReader.GetTableMetadataOffset(TableIndex.MethodDef); + var tableRowSize = metadataReader.GetTableRowSize(TableIndex.MethodDef); + var symbolIndex = 0; + + foreach (var handle in metadataReader.MethodDefinitions) + { + var symbol = GetSymbolWithToken(symbols, ref symbolIndex, handle); + if (symbol == null || !IsIncluded(symbol, patterns)) + { + var def = metadataReader.GetMethodDefinition(handle); + + // reduce visibility so that the method is not visible outside the assembly: + var oldVisibility = def.Attributes & MethodAttributes.MemberAccessMask; + var newVisibility = MethodAttributes.Private; + if (oldVisibility == newVisibility) + { + continue; + } + + // Row: RvaOffset (4B), ImplAttributes (2B), Attributes (2B), ... + var offset = tableOffset + (MetadataTokens.GetRowNumber(handle) - 1) * tableRowSize + sizeof(uint) + sizeof(ushort); +#if DEBUG + writer.BaseStream.Position = offset; + Debug.Assert((MethodAttributes)ReadUInt16(writer.BaseStream) == def.Attributes); +#endif + writer.BaseStream.Position = offset; + Debug.Assert(BitConverter.IsLittleEndian); + writer.Write((ushort)(def.Attributes & ~MethodAttributes.MemberAccessMask | newVisibility)); + } + } + } + + private static unsafe void UpdateFieldDefinitions(BinaryWriter writer, MetadataReader metadataReader, ImmutableArray patterns, ImmutableArray symbols, int metadataOffset) + { + var tableOffset = metadataOffset + metadataReader.GetTableMetadataOffset(TableIndex.Field); + var tableRowSize = metadataReader.GetTableRowSize(TableIndex.Field); + var symbolIndex = 0; + + foreach (var handle in metadataReader.FieldDefinitions) + { + var symbol = GetSymbolWithToken(symbols, ref symbolIndex, handle); + if (symbol == null || !IsIncluded(symbol, patterns)) + { + var def = metadataReader.GetFieldDefinition(handle); + + // reduce visibility so that the field is not visible outside the assembly: + var oldVisibility = def.Attributes & FieldAttributes.FieldAccessMask; + var newVisibility = FieldAttributes.Private; + if (oldVisibility == newVisibility) + { + continue; + } + + // Row: Attributes (2B), ... + var offset = tableOffset + (MetadataTokens.GetRowNumber(handle) - 1) * tableRowSize + 0; +#if DEBUG + writer.BaseStream.Position = offset; + Debug.Assert((FieldAttributes)ReadUInt16(writer.BaseStream) == def.Attributes); +#endif + writer.BaseStream.Position = offset; + Debug.Assert(BitConverter.IsLittleEndian); + writer.Write((ushort)(def.Attributes & ~FieldAttributes.FieldAccessMask | newVisibility)); + } + } + } + + private static uint ReadUInt32(Stream stream) + => unchecked((uint)(stream.ReadByte() | stream.ReadByte() << 8 | stream.ReadByte() << 16 | stream.ReadByte() << 24)); + + private static uint ReadUInt16(Stream stream) + => unchecked((uint)(stream.ReadByte() | stream.ReadByte() << 8)); + + private static unsafe Guid ReadGuid(Stream stream) + { + var buffer = new byte[sizeof(Guid)]; + Debug.Assert(stream.Read(buffer, 0, buffer.Length) == buffer.Length); + fixed (byte* ptr = buffer) + { + var reader = new BlobReader(ptr, buffer.Length); + return reader.ReadGuid(); + } + } + + private static unsafe void WriteGuid(BinaryWriter writer, Guid guid) + { + var buffer = new byte[sizeof(Guid)]; + var blob = new BlobWriter(buffer); + blob.WriteGuid(guid); + writer.Write(buffer, 0, buffer.Length); + } + + private static Guid CreateMvid(Stream stream) + { + stream.Position = 0; + using var sha = SHA256.Create(); + return BlobContentId.FromHash(sha.ComputeHash(stream)).Guid; + } +} diff --git a/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj b/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj new file mode 100644 index 0000000000000..9e28511dab63f --- /dev/null +++ b/src/Tools/SemanticSearch/BuildTask/SemanticSearch.BuildTask.csproj @@ -0,0 +1,26 @@ + + + Library + $(NetRoslyn);net472 + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.CSharp.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.CSharp.txt new file mode 100644 index 0000000000000..5f282702bb03e --- /dev/null +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.CSharp.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.txt new file mode 100644 index 0000000000000..6d657b0290b8a --- /dev/null +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/Microsoft.CodeAnalysis.txt @@ -0,0 +1,5 @@ ++T:* +-M:Microsoft.CodeAnalysis.MetadataReference.CreateFromFile* +-T:Microsoft.CodeAnalysis.FileSystemExtensions +-T:Microsoft.CodeAnalysis.CommandLineParser +-T:Microsoft.CodeAnalysis.DesktopStrongNameProvider \ No newline at end of file diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Collections.Immutable.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Collections.Immutable.txt new file mode 100644 index 0000000000000..5f282702bb03e --- /dev/null +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Collections.Immutable.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Collections.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Collections.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Linq.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Linq.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Runtime.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Runtime.txt new file mode 100644 index 0000000000000..ce07ea552448d --- /dev/null +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/ApiSet/System.Runtime.txt @@ -0,0 +1,61 @@ ++T:System.* +-T:System.Activator +-T:System.AppDomain* +-T:System.AssemblyLoad* +-T:System.AppContext +-TM:System.Environment.* ++M:System.Environment.get_CurrentManagedThreadId ++M:System.Environment.get_NewLine +-T:System.EnvironmentVariableTarget +-T:System.GC* +-T:System.LoaderOptimization* +-T:System.MarshalByRefObject +-T:System.MTAThreadAttribute +-T:System.STAThreadAttribute +-T:System.ThreadStaticAttribute +-T:System.Diagnostics.Debugger +-M:System.Globalization.CultureInfo.set_* +-M:System.Type.* ++M:System.Type.Name ++M:System.Type.FullName ++M:System.Type.AssemblyQualifiedName +-T:System.IO.* ++T:System.IO.BinaryReader ++T:System.IO.BinaryWriter ++T:System.IO.BufferedStream ++T:System.IO.EndOfStreamException ++T:System.IO.InvalidDataException ++T:System.IO.MemoryStream ++T:System.IO.Stream ++T:System.IO.StreamReader ++T:System.IO.StreamWriter ++T:System.IO.StringReader ++T:System.IO.StringWriter ++T:System.IO.TextReader ++T:System.IO.TextWriter + +-T:System.Net.* +-T:System.Reflection.* +-T:System.Resources.* + +-T:System.Runtime.* ++T:System.Runtime.CompilerServices.* + +-T:System.Security.* + +-T:System.Threading.* ++T:System.Threading.CancellationToken + ++T:System.Threading.Tasks.* +-M:System.Threading.Tasks.Task.* +-M:System.Threading.Tasks.Task`1.* ++M:System.Threading.Tasks.Task*.get_* ++M:System.Threading.Tasks.Task*.ConfigureAwait(*) ++M:System.Threading.Tasks.Task*.GetAwaiter ++M:System.Threading.Tasks.Task*.From*(*) ++M:System.Threading.Tasks.Task*.Dispose* +-T:System.Threading.Tasks.TaskFactory* +-T:System.Threading.Tasks.TaskScheduler + ++T:System.Threading.Tasks.ConfigureAwaitOptions ++T:System.Threading.Tasks.TaskAsyncEnumerableExtensions \ No newline at end of file diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj b/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj new file mode 100644 index 0000000000000..0ff3c9cb5533c --- /dev/null +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj @@ -0,0 +1,69 @@ + + + Library + $(NetRoslyn) + + <_BuildTaskTfm Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 + <_BuildTaskTfm Condition="'$(MSBuildRuntimeType)' == 'Core'">$(NetRoslyn) + + + + + + + + + + + + + + + + + + <_OutputDir>$(IntermediateOutputPath)GeneratedRefAssemblies\ + + + + <_InputReference Include="@(ReferencePath)" + Condition="'%(ReferencePath.FrameworkReferenceName)' == 'Microsoft.NETCore.App' or + '%(ReferencePath.FileName)' == 'System.Collections.Immutable' or + '%(ReferencePath.FileName)' == 'Microsoft.CodeAnalysis' or + '%(ReferencePath.FileName)' == 'Microsoft.CodeAnalysis.CSharp'" /> + + + + <_InputFile Include="@(ApiSet)" /> + <_InputFile Include="@(_InputReference)" /> + + <_OutputFile Include="@(ApiSet->'$(_OutputDir)%(FileName).dll')" /> + + + + + + + + + + + + + + + + + + <_PublishProjectOutputGroupOutput Include="@(_OutputFile)" /> + + + diff --git a/src/Tools/SemanticSearch/Tests/GenerateFilteredReferenceAssembliesTaskTests.cs b/src/Tools/SemanticSearch/Tests/GenerateFilteredReferenceAssembliesTaskTests.cs new file mode 100644 index 0000000000000..9169590df99ff --- /dev/null +++ b/src/Tools/SemanticSearch/Tests/GenerateFilteredReferenceAssembliesTaskTests.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Tools.UnitTests; + +public sealed class GenerateFilteredReferenceAssembliesTaskTests : CSharpTestBase +{ + [Theory] + [InlineData("")] + [InlineData("\t")] + [InlineData("\t#\t")] + [InlineData("#")] + [InlineData("#+")] + [InlineData("#abc")] + [InlineData("#𫚭鿯龻蝌灋齅ㄥ﹫䶱ན།ىي꓂")] // GB18030 + public void ParseApiPatterns_Empty(string value) + { + var errors = new List<(string message, int line)>(); + var patterns = new List(); + GenerateFilteredReferenceAssembliesTask.ParseApiPatterns([value], errors, patterns); + Assert.Empty(errors); + Assert.Empty(patterns); + } + + [Theory] + [InlineData("+")] + [InlineData("-")] + [InlineData("-#")] + public void ParseApiPatterns_Error_ExpectedMetadataName(string value) + { + var errors = new List<(string message, int line)>(); + var patterns = new List(); + GenerateFilteredReferenceAssembliesTask.ParseApiPatterns([value], errors, patterns); + AssertEx.Equal(new[] { ("expected metadata name", 1) }, errors); + Assert.Empty(patterns); + } + + [Theory] + [InlineData("E")] + [InlineData("P")] + [InlineData("N")] + [InlineData("X")] + internal void ParseApiPatterns_Error_UnexpectedSymbolKind(string kind) + { + var errors = new List<(string message, int line)>(); + var patterns = new List(); + GenerateFilteredReferenceAssembliesTask.ParseApiPatterns([kind + ":*"], errors, patterns); + AssertEx.Equal(new[] { ($"unexpected symbol kind: '{kind}'", 1) }, errors); + Assert.Empty(patterns); + } + + [Theory] + [InlineData("*", true, SymbolKindFlags.NamedType, @".*")] + [InlineData("?", true, SymbolKindFlags.NamedType, @"\?")] + [InlineData("%", true, SymbolKindFlags.NamedType, @"%")] + [InlineData("<", true, SymbolKindFlags.NamedType, @"<")] + [InlineData("a b c", true, SymbolKindFlags.NamedType, @"a\ b\ c")] + [InlineData("a b c#", true, SymbolKindFlags.NamedType, @"a\ b\ c")] + [InlineData(" a b #c", true, SymbolKindFlags.NamedType, @"a\ b")] + [InlineData(" + System.IO", true, SymbolKindFlags.NamedType, @"System\.IO")] + [InlineData("+System.IO.*", true, SymbolKindFlags.NamedType, @"System\.IO\..*")] + [InlineData(" -System.IO.**", false, SymbolKindFlags.NamedType, @"System\.IO\..*.*")] + [InlineData("- System.IO.* *", false, SymbolKindFlags.NamedType, @"System\.IO\..*\ .*")] + [InlineData("𫚭鿯龻蝌灋齅ㄥ﹫䶱ན།ىي꓂", true, SymbolKindFlags.NamedType, @"𫚭鿯龻蝌灋齅ㄥ﹫䶱ན།ىي꓂")] // GB18030 + [InlineData("M:*", true, SymbolKindFlags.Method, @".*")] + [InlineData("M:?", true, SymbolKindFlags.Method, @"\?")] + [InlineData("M: a b #c", true, SymbolKindFlags.Method, @"a\ b")] + [InlineData("+M: System.IO", true, SymbolKindFlags.Method, @"System\.IO")] + [InlineData("+M: System.IO.Path.F(*)", true, SymbolKindFlags.Method, @"System\.IO\.Path\.F\(.*\)")] + internal void ParseApiPatterns(string value, bool isIncluded, SymbolKindFlags symbolKinds, string pattern) + { + var errors = new List<(string message, int line)>(); + var patterns = new List(); + GenerateFilteredReferenceAssembliesTask.ParseApiPatterns([value], errors, patterns); + Assert.Empty(errors); + + AssertEx.Equal(new[] { (symbolKinds, $"^{pattern}$", isIncluded) }, patterns.Select(p => (p.SymbolKinds, p.MetadataNamePattern.ToString(), p.IsIncluded))); + } + + [Fact] + public void Types() + { + var libSource = CreateCompilation(""" + namespace N + { + public class C + { + public class D; + } + } + + namespace M + { + public class E; + public class E; + public class E; + } + """); + + var dir = Temp.CreateDirectory(); + var libImage = libSource.EmitToArray(new EmitOptions(metadataOnly: true)).ToArray(); + + var patterns = ImmutableArray.Create( + new ApiPattern(SymbolKindFlags.NamedType, new Regex(@"M\.E.*"), IsIncluded: true), + new ApiPattern(SymbolKindFlags.NamedType, new Regex(@"M\.E`1"), IsIncluded: false)); + + GenerateFilteredReferenceAssembliesTask.Rewrite(libImage, patterns); + + var libRef = MetadataReference.CreateFromImage(libImage); + + var c = CreateCompilation(""" + N.C.D d = null; + M.E e1 = null; + M.E e2 = null; + M.E e3 = null; + """, references: [libRef], TestOptions.DebugExe); + + c.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).Verify( + // (1,3): error CS0122: 'C' is inaccessible due to its protection level + // N.C.D d = null; + Diagnostic(ErrorCode.ERR_BadAccess, "C").WithArguments("N.C").WithLocation(1, 3), + // (3,3): error CS0122: 'E' is inaccessible due to its protection level + // M.E e2 = null; + Diagnostic(ErrorCode.ERR_BadAccess, "E").WithArguments("M.E").WithLocation(3, 3)); + } + + [Fact] + public void Interface() + { + var libSource = CreateCompilation(""" + public interface I + { + public void M1(); + public void M2(); + } + + public class C : I + { + public C() => throw null; + public void M1() => throw null; + public void M2() => throw null; + } + """); + + var dir = Temp.CreateDirectory(); + var libImage = libSource.EmitToArray(new EmitOptions(metadataOnly: true)).ToArray(); + + var patterns = ImmutableArray.Create( + new ApiPattern(SymbolKindFlags.NamedType, new Regex(@".*"), IsIncluded: true), + new ApiPattern(SymbolKindFlags.Method, new Regex(@"I.M1"), IsIncluded: false)); + + GenerateFilteredReferenceAssembliesTask.Rewrite(libImage, patterns); + + var libRef = MetadataReference.CreateFromImage(libImage); + + var c = CreateCompilation(""" + I i = new C(); + i.M1(); + i.M2(); + + C c = new C(); + c.M1(); + c.M2(); + """, references: [libRef], TestOptions.DebugExe); + + c.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).Verify( + // (2,3): error CS0122: 'I.M1()' is inaccessible due to its protection level + // i.M1(); + Diagnostic(ErrorCode.ERR_BadAccess, "M1").WithArguments("I.M1()").WithLocation(2, 3)); + } + + [Fact] + public void Property() + { + var libSource = CreateCompilation(""" + public class C + { + public int P1 { get; } + public int P2 { get; set; } + public int P3 { get; protected set; } + public int P4 { get; private protected set; } + } + """); + + var dir = Temp.CreateDirectory(); + var libImage = libSource.EmitToArray(new EmitOptions(metadataOnly: true)).ToArray(); + + var patterns = ImmutableArray.Create( + new ApiPattern(SymbolKindFlags.NamedType, new Regex(@".*"), IsIncluded: true), + new ApiPattern(SymbolKindFlags.Method, new Regex(@"C\.get_.*"), IsIncluded: false), + new ApiPattern(SymbolKindFlags.Method, new Regex(@"C\.set_.*"), IsIncluded: false), + new ApiPattern(SymbolKindFlags.Method, new Regex(@"C\.get_P2"), IsIncluded: true)); + + GenerateFilteredReferenceAssembliesTask.Rewrite(libImage, patterns); + + var libRef = MetadataReference.CreateFromImage(libImage); + + var c = CreateCompilation(""" + var d = new D(); + d.F(); + + class D : C + { + public int F() + { + P2 = 2; + P3 = 3; + P4 = 4; + + return P1 + P2 + P3 + P4; + } + } + """, references: [libRef], TestOptions.DebugExe); + + c.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).Verify( + // (8,9): error CS0200: Property or indexer 'C.P2' cannot be assigned to -- it is read only + // P2 = 2; + Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "P2").WithArguments("C.P2").WithLocation(8, 9), + // (9,9): error CS0103: The name 'P3' does not exist in the current context + // P3 = 3; + Diagnostic(ErrorCode.ERR_NameNotInContext, "P3").WithArguments("P3").WithLocation(9, 9), + // (10,9): error CS0103: The name 'P4' does not exist in the current context + // P4 = 4; + Diagnostic(ErrorCode.ERR_NameNotInContext, "P4").WithArguments("P4").WithLocation(10, 9), + // (12,16): error CS0103: The name 'P1' does not exist in the current context + // return P1 + P2 + P3 + P4; + Diagnostic(ErrorCode.ERR_NameNotInContext, "P1").WithArguments("P1").WithLocation(12, 16), + // (12,26): error CS0103: The name 'P3' does not exist in the current context + // return P1 + P2 + P3 + P4; + Diagnostic(ErrorCode.ERR_NameNotInContext, "P3").WithArguments("P3").WithLocation(12, 26), + // (12,31): error CS0103: The name 'P4' does not exist in the current context + // return P1 + P2 + P3 + P4; + Diagnostic(ErrorCode.ERR_NameNotInContext, "P4").WithArguments("P4").WithLocation(12, 31)); + } +} diff --git a/src/Tools/SemanticSearch/Tests/SemanticSearch.BuildTask.UnitTests.csproj b/src/Tools/SemanticSearch/Tests/SemanticSearch.BuildTask.UnitTests.csproj new file mode 100644 index 0000000000000..82d137d640c9c --- /dev/null +++ b/src/Tools/SemanticSearch/Tests/SemanticSearch.BuildTask.UnitTests.csproj @@ -0,0 +1,11 @@ + + + Library + $(NetRoslyn);net472 + + + + + + + diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.cs.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.cs.xlf index 690ce89eb11d0..af4d846710f81 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.cs.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.cs.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interactive C# Semantic Search - C# Semantic Search + C# Sémantické vyhledávání Initialize Interactive with Project - Initialize Interactive with Project + Inicializovat Interactive přes projekt diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.de.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.de.xlf index 7b7cccfec5b52..abe0d2cc230d5 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.de.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.de.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interactive C# Semantic Search - C# Semantic Search + Semantische C#-Suche Initialize Interactive with Project - Initialize Interactive with Project + Interaktiv mit dem Projekt initialisieren diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.es.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.es.xlf index 9b962e84d5f16..119e1eaf90037 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.es.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.es.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# interactivo C# Semantic Search - C# Semantic Search + Búsqueda semántica de C# Initialize Interactive with Project - Initialize Interactive with Project + Inicializar el elemento interactivo con el proyecto diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.fr.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.fr.xlf index 6dae65fe61cbf..fd9b03fce2517 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.fr.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.fr.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interactive C# Semantic Search - C# Semantic Search + Recherche sémantique C# Initialize Interactive with Project - Initialize Interactive with Project + Initialiser Interactive avec le projet diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.it.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.it.xlf index ededa633cd4d3..6bca98519df69 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.it.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.it.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interactive C# Semantic Search - C# Semantic Search + Ricerca semantica C# Initialize Interactive with Project - Initialize Interactive with Project + Inizializza Interactive con il progetto diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ja.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ja.xlf index 6b3b47991c93d..c7aeecc25419f 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ja.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ja.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# インタラクティブ C# Semantic Search - C# Semantic Search + C# セマンティック検索 Initialize Interactive with Project - Initialize Interactive with Project + プロジェクトでインタラクティブを初期化 diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ko.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ko.xlf index 2ebb6cb7a2b11..fad466b8aaffa 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ko.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ko.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# 대화형 C# Semantic Search - C# Semantic Search + C# 의미 체계 검색 Initialize Interactive with Project - Initialize Interactive with Project + 프로젝트에서 Interactive 초기화 diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pl.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pl.xlf index 1829759bfe220..49dad14d012f0 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pl.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pl.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interactive C# Semantic Search - C# Semantic Search + Wyszukiwanie semantyczne C# Initialize Interactive with Project - Initialize Interactive with Project + Inicjuj środowisko interaktywne z projektem diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pt-BR.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pt-BR.xlf index 174a159ee952c..c67e3046086da 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pt-BR.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.pt-BR.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interativo C# Semantic Search - C# Semantic Search + Pesquisa semântica do C# Initialize Interactive with Project - Initialize Interactive with Project + Inicializar Interativo com o Projeto diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ru.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ru.xlf index 03fb7affc7311..99f11773333c6 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ru.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.ru.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Interactive C# Semantic Search - C# Semantic Search + Семантический поиск C# Initialize Interactive with Project - Initialize Interactive with Project + Инициализировать интерактивное окно с проектом diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.tr.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.tr.xlf index b4d9c1193c701..5e5c9ef2e789c 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.tr.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.tr.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# Etkileşimli C# Semantic Search - C# Semantic Search + C# Anlamsal Arama Initialize Interactive with Project - Initialize Interactive with Project + Projeyi Etkileşimli Pencerede Başlat diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hans.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hans.xlf index c34d2b5c61d76..e9b8dff57c8cb 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hans.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hans.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# 交互窗口 C# Semantic Search - C# Semantic Search + C# 语义搜索 Initialize Interactive with Project - Initialize Interactive with Project + 对交互窗口进行项目初始化 diff --git a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hant.xlf b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hant.xlf index 7b0bf4cdbfba8..bfd19c743cf32 100644 --- a/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hant.xlf +++ b/src/VisualStudio/CSharp/Impl/.vsextension/xlf/string-resources.json.zh-Hant.xlf @@ -4,17 +4,17 @@ C# Interactive - C# Interactive + C# 互動 C# Semantic Search - C# Semantic Search + C# 語意搜尋 Initialize Interactive with Project - Initialize Interactive with Project + 使用專案將 Interactive 初始化 diff --git a/src/VisualStudio/CSharp/Impl/CSharpPackage.cs b/src/VisualStudio/CSharp/Impl/CSharpPackage.cs index ccae4e77195f6..30bfc38cee381 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpPackage.cs +++ b/src/VisualStudio/CSharp/Impl/CSharpPackage.cs @@ -65,7 +65,6 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke try { await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(true); - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); this.RegisterService(async ct => diff --git a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx index 4d5832f4d4f0b..e3b595303567f 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx +++ b/src/VisualStudio/CSharp/Impl/CSharpVSResources.resx @@ -637,4 +637,19 @@ Allow blank line after token in arrow expression clause + + Semantic Search + + + Run Query + + + Cancel Query + + + Run (Ctrl+Enter) + + + Cancel (Escape) + \ No newline at end of file diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml index e1416d98bc310..223ce547c0247 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml @@ -53,14 +53,8 @@ - - - - + Content="{x:Static local:AdvancedOptionPageStrings.Option_run_code_analysis_in_separate_process}" /> + @@ -187,6 +181,8 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Show_guides_for_declaration_level_constructs}" /> + @@ -218,6 +214,8 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Report_invalid_placeholders_in_string_dot_format_calls}" /> + diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index 859461ef020cb..e0f1dd32891b3 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -67,7 +67,6 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(on_the_right_edge_of_the_editor_window, InlineDiagnosticsOptionsStorage.Location, InlineDiagnosticsLocations.PlacedAtEndOfEditor, LanguageNames.CSharp); BindToOption(Run_code_analysis_in_separate_process, RemoteHostOptionsStorage.OOP64Bit); - BindToOption(Run_code_analysis_on_dotnet, RemoteHostOptionsStorage.OOPCoreClr); BindToOption(Analyze_source_generated_files, SolutionCrawlerOptionsStorage.EnableDiagnosticsInSourceGeneratedFiles, () => { @@ -124,6 +123,7 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon // Block Structure Guides BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp); BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp); + BindToOption(Show_guides_for_comments_and_preprocessor_regions, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, LanguageNames.CSharp); // Comments BindToOption(GenerateXmlDocCommentsForTripleSlash, DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, LanguageNames.CSharp); @@ -137,6 +137,7 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(Fix_text_pasted_into_string_literals_experimental, StringCopyPasteOptionsStorage.AutomaticallyFixStringContentsOnPaste, LanguageNames.CSharp); BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, IdeAnalyzerOptionsStorage.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.CSharp); BindToOption(Underline_reassigned_variables, ClassificationOptionsStorage.ClassifyReassignedVariables, LanguageNames.CSharp); + BindToOption(Strike_out_obsolete_symbols, ClassificationOptionsStorage.ClassifyObsoleteSymbols, LanguageNames.CSharp); BindToOption(Enable_all_features_in_opened_files_from_source_generators, WorkspaceConfigurationOptionsStorage.EnableOpeningSourceGeneratedFilesInWorkspace, () => { // If the option has not been set by the user, check if the option is enabled from experimentation. @@ -277,15 +278,5 @@ private void EnterOutliningMode_Unchecked(object sender, RoutedEventArgs e) Collapse_metadata_signature_files_on_open.IsEnabled = false; Collapse_sourcelink_embedded_decompiled_files_on_open.IsEnabled = false; } - - private void Run_code_analysis_in_separate_process_Checked(object sender, RoutedEventArgs e) - { - Run_code_analysis_on_dotnet.IsEnabled = true; - } - - private void Run_code_analysis_in_separate_process_Unchecked(object sender, RoutedEventArgs e) - { - Run_code_analysis_on_dotnet.IsEnabled = false; - } } } diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs index 2f53e9718508f..c3c9f50d05806 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs @@ -78,9 +78,6 @@ public static string Option_Always_use_default_symbol_servers_for_navigation public static string Option_run_code_analysis_in_separate_process => ServicesVSResources.Run_code_analysis_in_separate_process_requires_restart; - public static string Option_run_code_analysis_on_dotnet - => ServicesVSResources.Run_code_analysis_on_latest_dotnet_requires_restart; - public static string Option_analyze_source_generated_files => ServicesVSResources.Analyze_source_generated_files; @@ -150,6 +147,9 @@ public static string Option_DisplayLineSeparators public static string Option_Underline_reassigned_variables => ServicesVSResources.Underline_reassigned_variables; + public static string Option_Strike_out_obsolete_symbols + => ServicesVSResources.Strike_out_obsolete_symbols; + public static string Option_DontPutOutOrRefOnStruct => CSharpVSResources.Don_t_put_ref_or_out_on_custom_struct; @@ -261,6 +261,9 @@ public static string Option_Show_guides_for_declaration_level_constructs public static string Option_Show_guides_for_code_level_constructs => ServicesVSResources.Show_guides_for_code_level_constructs; + public static string Option_Show_guides_for_comments_and_preprocessor_regions + => ServicesVSResources.Show_guides_for_comments_and_preprocessor_regions; + public static string Option_Fading => ServicesVSResources.Fading; diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/OpenSemanticSearchWindowCommand.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/OpenSemanticSearchWindowCommand.cs new file mode 100644 index 0000000000000..53096f1cefd6a --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/OpenSemanticSearchWindowCommand.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Commands; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +/// +/// Implements View/Other Windows/Semantic Search command. +/// +[VisualStudioContribution] +internal sealed class OpenSemanticSearchWindowCommand : Command +{ + public override CommandConfiguration CommandConfiguration => new("%CSharpLanguageServiceExtension.OpenSemanticSearchWindow.DisplayName%") + { + Icon = new(ImageMoniker.KnownValues.FindSymbol, IconSettings.IconAndText), + Placements = new[] { CommandPlacement.KnownPlacements.ViewOtherWindowsMenu.WithPriority(0x8010) }, + VisibleWhen = ActivationConstraint.UIContext(Guid.Parse(SemanticSearchFeatureFlag.UIContextId)) + }; + + public override Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + => Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken); +} diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchDocumentNavigationService.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchDocumentNavigationService.cs new file mode 100644 index 0000000000000..1d8310925273f --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchDocumentNavigationService.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.SemanticSearch; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +[ExportWorkspaceService(typeof(IDocumentNavigationService), WorkspaceKind.SemanticSearch), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SemanticSearchDocumentNavigationService(SemanticSearchToolWindowImpl window) : IDocumentNavigationService +{ + public Task CanNavigateToSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) + => SpecializedTasks.True; + + public Task CanNavigateToPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) + => SpecializedTasks.False; + + public Task GetLocationForSpanAsync(Workspace workspace, DocumentId documentId, TextSpan textSpan, bool allowInvalidSpan, CancellationToken cancellationToken) + { + Debug.Assert(workspace is SemanticSearchWorkspace); + Debug.Assert(documentId == SemanticSearchUtilities.GetQueryDocumentId(workspace.CurrentSolution)); + + return Task.FromResult(window.GetNavigableLocation(textSpan)); + } + + public Task GetLocationForPositionAsync(Workspace workspace, DocumentId documentId, int position, int virtualSpace, CancellationToken cancellationToken) + => SpecializedTasks.Null(); +} diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs new file mode 100644 index 0000000000000..93895a56cfd32 --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.ToolWindows; +using Microsoft.VisualStudio.Extensibility.VSSdkCompatibility; +using Microsoft.VisualStudio.RpcContracts.RemoteUI; +using Dock = Microsoft.VisualStudio.Extensibility.ToolWindows.Dock; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +[VisualStudioContribution] +internal sealed class SemanticSearchToolWindow(MefInjection impl) : ToolWindow +{ + /// + /// HACK: Id of the tool window needed for finding tool window frame. This is created by Gladstone by hashing the full type names of + /// and types. + /// + internal static readonly Guid Id = new("91ef2fc9-e39d-1962-9b55-7047b01b40f7"); + + // Initialized by InitializeAsync + private SemanticSearchToolWindowImpl _impl = null!; + + private IRemoteUserControl? _lazyContent; + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _lazyContent?.Dispose(); + } + + base.Dispose(disposing); + } + + public override async Task InitializeAsync(CancellationToken cancellationToken) + { + await base.InitializeAsync(cancellationToken).ConfigureAwait(false); + + Title = string.Format(ServicesVSResources.Semantic_search_0, LanguageNames.CSharp); + + _impl = await impl.GetServiceAsync().ConfigureAwait(false); + } + + public override ToolWindowConfiguration ToolWindowConfiguration => new() + { + Placement = ToolWindowPlacement.Floating, + DockDirection = Dock.Bottom, + AllowAutoCreation = true, + }; + + public override async Task GetContentAsync(CancellationToken cancellationToken) + { + var content = _lazyContent; + if (content != null) + { + return content; + } + + var element = await _impl.InitializeAsync(cancellationToken).ConfigureAwait(false); + Interlocked.CompareExchange(ref _lazyContent, new WpfControlWrapper(element), null); + return _lazyContent; + } +} diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs new file mode 100644 index 0000000000000..7c4a4ccee01ee --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs @@ -0,0 +1,554 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Markup; +using System.Windows.Media; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Host; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.SemanticSearch; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Imaging; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Threading; +using Microsoft.VisualStudio.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +using TextSpan = Microsoft.CodeAnalysis.Text.TextSpan; + +[Shared] +[Export(typeof(ISemanticSearchWorkspaceHost))] +[Export(typeof(SemanticSearchToolWindowImpl))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SemanticSearchToolWindowImpl( + IHostWorkspaceProvider hostWorkspaceProvider, + IThreadingContext threadingContext, + ITextEditorFactoryService textEditorFactory, + ITextDocumentFactoryService textDocumentFactory, + IContentTypeRegistryService contentTypeRegistry, + IVsEditorAdaptersFactoryService vsEditorAdaptersFactoryService, + IAsynchronousOperationListenerProvider listenerProvider, + IGlobalOptionService globalOptions, + VisualStudioWorkspace workspace, + IStreamingFindUsagesPresenter resultsPresenter, + IVsService vsUIShellProvider) : ISemanticSearchWorkspaceHost, OptionsProvider +{ + private const int ToolBarHeight = 26; + private const int ToolBarButtonSize = 20; + + private static readonly Lazy s_buttonTemplate = new(CreateButtonTemplate); + + private readonly IContentType _contentType = contentTypeRegistry.GetContentType(ContentTypeNames.CSharpContentType); + private readonly IAsynchronousOperationListener _asyncListener = listenerProvider.GetListener(FeatureAttribute.SemanticSearch); + + private readonly Lazy _semanticSearchWorkspace + = new(() => new SemanticSearchEditorWorkspace( + hostWorkspaceProvider.Workspace.Services.HostServices, + CSharpSemanticSearchUtilities.Configuration, + threadingContext, + listenerProvider)); + + // access interlocked: + private volatile CancellationTokenSource? _pendingExecutionCancellationSource; + + // Access on UI thread only: + private Button? _executeButton; + private Button? _cancelButton; + private IWpfTextView? _textView; + private ITextBuffer? _textBuffer; + + public async Task InitializeAsync(CancellationToken cancellationToken) + { + // TODO: replace with XAML once we can represent the editor as a XAML element + // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1927626 + + // TODO: Add toolbar and convert Execute and Cancel buttons to commands. + // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1978331 + + // The WPF control needs to be created on an UI thread + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var vsUIShell = await vsUIShellProvider.GetValueAsync(cancellationToken).ConfigureAwait(false); + + var textViewHost = CreateTextViewHost(vsUIShell); + var textViewControl = textViewHost.HostControl; + _textView = textViewHost.TextView; + _textBuffer = textViewHost.TextView.TextBuffer; + + // enable LSP: + Contract.ThrowIfFalse(textDocumentFactory.TryGetTextDocument(_textBuffer, out var textDocument)); + textDocument.Rename(SemanticSearchUtilities.GetDocumentFilePath(LanguageNames.CSharp)); + + var toolWindowGrid = new Grid(); + toolWindowGrid.ColumnDefinitions.Add(new ColumnDefinition()); + toolWindowGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(ToolBarHeight, GridUnitType.Pixel) }); + toolWindowGrid.RowDefinitions.Add(new RowDefinition()); + + var toolbarGrid = new Grid(); + + // Set dynamically, so that it gets refreshed when theme changes: + toolbarGrid.SetResourceReference(Control.BackgroundProperty, EnvironmentColors.CommandBarGradientBrushKey); + + toolbarGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(ToolBarHeight, GridUnitType.Pixel) }); + toolbarGrid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(ToolBarHeight, GridUnitType.Pixel) }); + toolbarGrid.ColumnDefinitions.Add(new ColumnDefinition()); + + var executeButton = CreateButton( + KnownMonikers.Run, + automationName: CSharpVSResources.RunQuery, + acceleratorKey: "Ctrl+Enter", + toolTip: CSharpVSResources.RunQueryCommandToolTip); + + executeButton.Click += (_, _) => RunQuery(); + _executeButton = executeButton; + + var cancelButton = CreateButton( + KnownMonikers.Stop, + automationName: CSharpVSResources.CancelQuery, + acceleratorKey: "Escape", + toolTip: CSharpVSResources.CancelQueryCommandToolTip); + + cancelButton.Click += (_, _) => CancelQuery(); + cancelButton.IsEnabled = false; + _cancelButton = cancelButton; + + toolWindowGrid.Children.Add(toolbarGrid); + toolWindowGrid.Children.Add(textViewControl); + toolbarGrid.Children.Add(executeButton); + toolbarGrid.Children.Add(cancelButton); + + // placement within the tool window grid: + + Grid.SetRow(textViewControl, 1); + Grid.SetColumn(textViewControl, 0); + + Grid.SetRow(toolbarGrid, 0); + Grid.SetColumn(toolbarGrid, 0); + + // placement within the toolbar grid: + + Grid.SetRow(executeButton, 0); + Grid.SetColumn(executeButton, 0); + + Grid.SetRow(cancelButton, 0); + Grid.SetColumn(cancelButton, 1); + + await TaskScheduler.Default; + + await _semanticSearchWorkspace.Value.OpenQueryDocumentAsync(_textBuffer, cancellationToken).ConfigureAwait(false); + + return toolWindowGrid; + } + + SemanticSearchWorkspace ISemanticSearchWorkspaceHost.Workspace => _semanticSearchWorkspace.Value; + + private static Button CreateButton( + Imaging.Interop.ImageMoniker moniker, + string automationName, + string acceleratorKey, + string toolTip) + { + var image = new CrispImage() + { + Moniker = moniker, + Width = ToolBarButtonSize - 4, + Height = ToolBarButtonSize - 4, + ToolTip = toolTip, + }; + + var holder = new ContentControl + { + Height = ToolBarButtonSize, + Width = ToolBarButtonSize, + Background = Brushes.Transparent, + BorderThickness = new Thickness(0, 0, 0, 0), + }; + + ImageThemingUtilities.SetImageBackgroundColor(holder, Colors.Transparent); + holder.Content = image; + + var button = new Button() + { + Template = s_buttonTemplate.Value, + Content = holder, + }; + + button.SetValue(AutomationProperties.NameProperty, automationName); + button.SetValue(AutomationProperties.AcceleratorKeyProperty, acceleratorKey); + + image.SetBinding(CrispImage.GrayscaleProperty, new Binding(UIElement.IsEnabledProperty.Name) + { + Source = button, + Converter = new NegateBooleanConverter() + }); + + return button; + } + + private static ControlTemplate CreateButtonTemplate() + { + var context = new ParserContext(); + context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation"); + context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml"); + context.XmlnsDictionary.Add("vsui", "clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"); + + return (ControlTemplate)XamlReader.Parse($$$""" + + + + + + + + + + + + + + + + + + + + + + + + """, context); + } + + private IWpfTextViewHost CreateTextViewHost(IVsUIShell vsUIShell) + { + Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); + + var toolWindowId = SemanticSearchToolWindow.Id; + ErrorHandler.ThrowOnFailure(vsUIShell.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fFrameOnly, ref toolWindowId, out var windowFrame)); + + var commandUiGuid = VSConstants.GUID_TextEditorFactory; + ErrorHandler.ThrowOnFailure(windowFrame.SetGuidProperty((int)__VSFPROPID.VSFPROPID_InheritKeyBindings, ref commandUiGuid)); + + var roleSet = textEditorFactory.CreateTextViewRoleSet( + PredefinedTextViewRoles.Analyzable, + PredefinedTextViewRoles.Editable, + PredefinedTextViewRoles.Interactive, + PredefinedTextViewRoles.Zoomable); + + var oleServiceProvider = (OLE.Interop.IServiceProvider)Shell.Package.GetGlobalService(typeof(OLE.Interop.IServiceProvider)); + + var bufferAdapter = vsEditorAdaptersFactoryService.CreateVsTextBufferAdapter(oleServiceProvider, _contentType); + bufferAdapter.InitializeContent("", 0); + + var textViewAdapter = vsEditorAdaptersFactoryService.CreateVsTextViewAdapter(oleServiceProvider, roleSet); + + // set properties to behave like a code window: + ErrorHandler.ThrowOnFailure(((IVsTextEditorPropertyCategoryContainer)textViewAdapter).GetPropertyCategory(DefGuidList.guidEditPropCategoryViewMasterSettings, out var propContainer)); + propContainer.SetProperty(VSEDITPROPID.VSEDITPROPID_ViewComposite_AllCodeWindowDefaults, true); + propContainer.SetProperty(VSEDITPROPID.VSEDITPROPID_ViewGlobalOpt_AutoScrollCaretOnTextEntry, true); + + ErrorHandler.ThrowOnFailure(textViewAdapter.Initialize( + (IVsTextLines)bufferAdapter, + IntPtr.Zero, + (uint)TextViewInitFlags.VIF_HSCROLL | (uint)TextViewInitFlags.VIF_VSCROLL | (uint)TextViewInitFlags3.VIF_NO_HWND_SUPPORT, + [new INITVIEW { fSelectionMargin = 0, fWidgetMargin = 0, fVirtualSpace = 0, fDragDropMove = 1 }])); + + var textViewHost = vsEditorAdaptersFactoryService.GetWpfTextViewHost(textViewAdapter); + Contract.ThrowIfNull(textViewHost); + + ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID.VSFPROPID_ViewHelper, textViewAdapter)); + + _ = new CommandFilter(this, textViewAdapter); + + return textViewHost; + } + + private bool IsExecutingUIState() + { + Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); + Contract.ThrowIfNull(_executeButton); + + return !_executeButton.IsEnabled; + } + + private void UpdateUIState() + { + Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); + Contract.ThrowIfNull(_executeButton); + Contract.ThrowIfNull(_cancelButton); + + // reflect the actual state in UI: + var isExecuting = _pendingExecutionCancellationSource != null; + + _executeButton.IsEnabled = !isExecuting; + _cancelButton.IsEnabled = isExecuting; + } + + private void CancelQuery() + { + Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); + Contract.ThrowIfFalse(IsExecutingUIState()); + + // The query might have been cancelled already but the UI may not be updated yet: + var pendingExecutionCancellationSource = Interlocked.Exchange(ref _pendingExecutionCancellationSource, null); + pendingExecutionCancellationSource?.Cancel(); + + UpdateUIState(); + } + + private void RunQuery() + { + Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); + Contract.ThrowIfFalse(!IsExecutingUIState()); + Contract.ThrowIfNull(_textBuffer); + + var cancellationSource = new CancellationTokenSource(); + + // Cancel execution that's in progress (if any) - may occur when UI state hasn't been updated yet based on the actual state. + Interlocked.Exchange(ref _pendingExecutionCancellationSource, cancellationSource)?.Cancel(); + + UpdateUIState(); + + var (presenterContext, presenterCancellationToken) = resultsPresenter.StartSearch(ServicesVSResources.Semantic_search_results, StreamingFindUsagesPresenterOptions.Default); + presenterCancellationToken.Register(() => cancellationSource?.Cancel()); + + var querySolution = _semanticSearchWorkspace.Value.CurrentSolution; + var queryDocument = SemanticSearchUtilities.GetQueryDocument(querySolution); + + var resultsObserver = new ResultsObserver(queryDocument, presenterContext); + + var completionToken = _asyncListener.BeginAsyncOperation(nameof(SemanticSearchToolWindow) + ".Execute"); + _ = ExecuteAsync(cancellationSource.Token).ReportNonFatalErrorAsync().CompletesAsyncOperation(completionToken); + + async Task ExecuteAsync(CancellationToken cancellationToken) + { + await TaskScheduler.Default; + + ExecuteQueryResult result = default; + + var canceled = false; + string? queryString = null; + + try + { + var solution = workspace.CurrentSolution; + + if (solution.ProjectIds is []) + { + await presenterContext.ReportNoResultsAsync(ServicesVSResources.Search_found_no_results_no_csharp_or_vb_projects_opened, cancellationToken).ConfigureAwait(false); + } + else + { + var query = await queryDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + queryString = query.ToString(); + + result = await RemoteSemanticSearchServiceProxy.ExecuteQueryAsync( + solution, + LanguageNames.CSharp, + queryString, + SemanticSearchUtilities.ReferenceAssembliesDirectory, + resultsObserver, + this, + cancellationToken).ConfigureAwait(false); + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) + { + result = new ExecuteQueryResult(e.Message); + } + catch (OperationCanceledException) + { + result = new ExecuteQueryResult(ServicesVSResources.Search_cancelled); + canceled = true; + } + finally + { + // Notify the presenter even if the search has been cancelled. + var completionToken = _asyncListener.BeginAsyncOperation(nameof(SemanticSearchToolWindow) + ".Completion"); + _ = CompleteSearchAsync().ReportNonFatalErrorAsync().CompletesAsyncOperation(completionToken); + + // Only clear pending source if it is the same as our source (otherwise, another execution has already kicked off): + Interlocked.CompareExchange(ref _pendingExecutionCancellationSource, value: null, cancellationSource); + + // Dispose cancellation source and clear it, so that the presenterCancellationToken handler won't attempt to cancel: + var source = Interlocked.Exchange(ref cancellationSource, null); + Contract.ThrowIfNull(source); + source.Dispose(); + + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None); + + // Update UI: + UpdateUIState(); + + async Task CompleteSearchAsync() + { + var errorMessage = result.ErrorMessage; + + if (errorMessage != null) + { + if (result.ErrorMessageArgs != null) + { + errorMessage = string.Format(errorMessage, result.ErrorMessageArgs); + } + + await presenterContext.ReportMessageAsync( + errorMessage, + canceled ? NotificationSeverity.Information : NotificationSeverity.Error, + CancellationToken.None).ConfigureAwait(false); + } + + await presenterContext.OnCompletedAsync(CancellationToken.None).ConfigureAwait(false); + + if (queryString != null) + { + Logger.Log(FunctionId.SemanticSearch_QueryExecution, KeyValueLogMessage.Create(map => + { + map["Query"] = new PiiValue(queryString); + + if (canceled) + { + map["Canceled"] = true; + } + else if (result.ErrorMessage != null) + { + map["ErrorMessage"] = result.ErrorMessage; + + if (result.ErrorMessageArgs != null) + { + map["ErrorMessageArgs"] = new PiiValue(string.Join("|", result.ErrorMessageArgs)); + } + } + + map["ExecutionTimeMilliseconds"] = (long)result.ExecutionTime.TotalMilliseconds; + map["EmitTime"] = (long)result.EmitTime.TotalMilliseconds; + })); + } + } + } + } + } + + public NavigableLocation GetNavigableLocation(TextSpan textSpan) + => new(async (options, cancellationToken) => + { + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + Contract.ThrowIfNull(_textView); + + var textSnapshot = _textView.TextBuffer.CurrentSnapshot; + var snapshotSpan = new SnapshotSpan(textSnapshot, textSpan.Start, textSpan.Length); + + _textView.Selection.Select(snapshotSpan, isReversed: false); + _textView.ViewScroller.EnsureSpanVisible(snapshotSpan, EnsureSpanVisibleOptions.AlwaysCenter); + + // Moving the caret must be the last operation involving surfaceBufferSpan because + // it might update the version number of textView.TextSnapshot (VB does line commit + // when the caret leaves a line which might cause pretty listing), which must be + // equal to surfaceBufferSpan.SnapshotSpan.Snapshot's version number. + _textView.Caret.MoveTo(snapshotSpan.Start); + + _textView.VisualElement.Focus(); + + return true; + }); + + public ValueTask GetOptionsAsync(Microsoft.CodeAnalysis.Host.LanguageServices languageServices, CancellationToken cancellationToken) + => new(globalOptions.GetClassificationOptions(languageServices.Language)); + + internal sealed class ResultsObserver(Document queryDocument, IFindUsagesContext presenterContext) : ISemanticSearchResultsObserver + { + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken) + => presenterContext.OnDefinitionFoundAsync(definition, cancellationToken); + + public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) + => presenterContext.ProgressTracker.AddItemsAsync(itemCount, cancellationToken); + + public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) + => presenterContext.ProgressTracker.ItemsCompletedAsync(itemCount, cancellationToken); + + public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) + => presenterContext.OnDefinitionFoundAsync( + new SearchExceptionDefinitionItem(exception.Message, exception.TypeName, exception.StackTrace, new DocumentSpan(queryDocument, exception.Span)), cancellationToken); + + public async ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) + { + foreach (var error in errors) + { + await presenterContext.OnDefinitionFoundAsync(new SearchCompilationFailureDefinitionItem(error, queryDocument), cancellationToken).ConfigureAwait(false); + } + } + } + + internal sealed class CommandFilter : IOleCommandTarget + { + private readonly SemanticSearchToolWindowImpl _window; + private readonly IOleCommandTarget _editorCommandTarget; + + public CommandFilter(SemanticSearchToolWindowImpl window, IVsTextView textView) + { + _window = window; + ErrorHandler.ThrowOnFailure(textView.AddCommandFilter(this, out _editorCommandTarget)); + } + + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) + => _editorCommandTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if (pguidCmdGroup == VSConstants.VSStd2K) + { + switch ((VSConstants.VSStd2KCmdID)nCmdID) + { + case VSConstants.VSStd2KCmdID.OPENLINEABOVE: + if (!_window.IsExecutingUIState()) + { + _window.RunQuery(); + return VSConstants.S_OK; + } + + break; + + case VSConstants.VSStd2KCmdID.CANCEL: + if (_window.IsExecutingUIState()) + { + _window.CancelQuery(); + return VSConstants.S_OK; + } + + break; + } + } + + return _editorCommandTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + } +} diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf index 827dfef8ab73a..467e1c0d5b777 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.cs.xlf @@ -57,6 +57,16 @@ C# Interactive + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Při otevření souboru sbalit usings @@ -172,6 +182,21 @@ Odebrat nepotřebné direktivy using + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Zobrazit nápovědy pro výrazy new diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf index 5fe89b000b3e4..3e008e33f0a9f 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.de.xlf @@ -57,6 +57,16 @@ C# Interactive + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Reduzieren von Usings beim Öffnen einer Datei @@ -172,6 +182,21 @@ Nicht benötigte Using-Direktiven entfernen + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Hinweise für new-Ausdrücke anzeigen diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf index c82475b89aae7..0d2916d3c3679 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.es.xlf @@ -57,6 +57,16 @@ C# interactivo + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Contraer instrucciones Using al abrir el archivo @@ -172,6 +182,21 @@ Eliminar instrucciones Using innecesarias + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Mostrar sugerencias para las expresiones "new" diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf index 1a90cb4f1859e..e9c9d2eb31b9d 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.fr.xlf @@ -57,6 +57,16 @@ C# Interactive + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Réduire les utilisations sur le fichier ouvert @@ -172,6 +182,21 @@ Supprimer les Usings inutiles + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Afficher les indicateurs pour les expressions 'new' diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf index 16058cee02eda..4c0d2484c40ee 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.it.xlf @@ -57,6 +57,16 @@ C# Interactive + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Comprimi gli utilizzi all'apertura del file @@ -172,6 +182,21 @@ Rimuovi istruzioni using non necessarie + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Mostra suggerimenti per le espressioni 'new' diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf index 2c1db671696b5..8002973babd12 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ja.xlf @@ -57,6 +57,16 @@ C# インタラクティブ + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open ファイルを開くときに usings を折りたたむ @@ -172,6 +182,21 @@ 不要な using の削除 + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions 'new' 式のヒントを表示する diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf index 361bd1d08c30a..71aeb34a7c9af 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ko.xlf @@ -57,6 +57,16 @@ C# 대화형 + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open 파일을 열 때 사용 축소 @@ -172,6 +182,21 @@ 불필요한 Using 제거 + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions 'new' 식에 대한 힌트 표시 diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf index 369bfaf224361..1084bed52c2b6 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pl.xlf @@ -57,6 +57,16 @@ C# Interactive + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Zwiń użycia przy otwieraniu pliku @@ -172,6 +182,21 @@ Usuń niepotrzebne użycia + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Pokaż wskazówki dla wyrażeń „new” diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf index fb04081eda7bd..9f5fa6e7e9397 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.pt-BR.xlf @@ -57,6 +57,16 @@ C# Interativo + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Recolher os usos no arquivo aberto @@ -172,6 +182,21 @@ Remover Usos Desnecessários + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Mostrar as dicas para as expressões 'new' diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf index 5bb3ba19a965e..7b8279944f595 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.ru.xlf @@ -57,6 +57,16 @@ C# Interactive + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Сворачивать директивы using при открытии файлов @@ -172,6 +182,21 @@ Удалить ненужные директивы using + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions Отображать подсказки для выражений "new" diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf index bbbf0a9425801..42bfac7a176f6 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.tr.xlf @@ -57,6 +57,16 @@ C# Etkileşimli + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open Dosya açıkken using'leri daralt @@ -172,6 +182,21 @@ Gereksiz Kullanımları Kaldır + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions 'new' ifadeleri için ipuçlarını göster diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf index 87692c2ad2c7b..7a228e88380fd 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hans.xlf @@ -57,6 +57,16 @@ C# 交互窗口 + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open 打开文件时折叠 using @@ -172,6 +182,21 @@ 删除不必要的 Using + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions 显示 "new" 表达式的提示 diff --git a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf index 1ad6b54c70796..b1c21d494c2b7 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/CSharpVSResources.zh-Hant.xlf @@ -57,6 +57,16 @@ C# 互動 + + Cancel Query + Cancel Query + + + + Cancel (Escape) + Cancel (Escape) + + Collapse usings on file open 在檔案開啟時折疊使用 @@ -172,6 +182,21 @@ 移除不必要的 Using + + Run Query + Run Query + + + + Run (Ctrl+Enter) + Run (Ctrl+Enter) + + + + Semantic Search + Semantic Search + + Show hints for 'new' expressions 顯示 'new' 運算式的提示 diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf index e6be2e1fc297a..2b76105cd8097 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.cs.xlf @@ -412,37 +412,37 @@ Zobrazit položky z neimportovaných oborů názvů (experimentální); Always add new line on enter - Always add new line on enter + Při stisku Enter vždy přidat nový řádek Always include snippets - Always include snippets + Vždy zahrnovat fragmenty Default - Default + Výchozí Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Zahrnout fragmenty kódu při zadání tabulátoru za identifikátor Never add new line on enter - Never add new line on enter + Při stisku Enter nikdy nepřidávat nový řádek Never include snippets - Never include snippets + Nikdy nezahrnovat fragmenty Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Při stisku Enter přidat nový řádek jenom po dopsání celého slova diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf index 29b1b9ac5c565..efc5cd572f7fe 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.de.xlf @@ -412,37 +412,37 @@ Elemente aus nicht importierten Namespaces anzeigen (experimentell); Always add new line on enter - Always add new line on enter + Nach Drücken der EINGABETASTE immer neue Zeile hinzufügen Always include snippets - Always include snippets + Schnipsel immer einschließen Default - Default + Standard Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Ausschnitte einschließen, wenn Tab nach einem Bezeichner eingegeben wird Never add new line on enter - Never add new line on enter + Nach Drücken der EINGABETASTE niemals neue Zeile hinzufügen Never include snippets - Never include snippets + Schnipsel nie einschließen Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Nach Drücken der EINGABETASTE nur nach dem Ende eines vollständigen Worts neue Zeile hinzufügen diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf index 7b5662fa39c84..6d46f239f5085 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.es.xlf @@ -412,37 +412,37 @@ Mostrar elementos de espacios de nombres no importados (experimental); Always add new line on enter - Always add new line on enter + Siempre agregar una nueva línea al pulsar Intro Always include snippets - Always include snippets + Incluir siempre fragmentos de código Default - Default + Predeterminado Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Incluir fragmentos de código cuando Tab se escriba después de un identificador Never add new line on enter - Never add new line on enter + Nunca agregar una nueva línea al pulsar Intro Never include snippets - Never include snippets + No incluir nunca fragmentos de código Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Solo agregar una nueva línea al pulsar Intro cuando se haya terminado de escribir completamente una palabra diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf index 32677cef8a0a9..a4e6bb816ef44 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.fr.xlf @@ -412,37 +412,37 @@ Afficher les éléments des espaces de noms qui ne sont pas importés (expérime Always add new line on enter - Always add new line on enter + Toujours ajouter une nouvelle ligne en appuyant sur Entrée Always include snippets - Always include snippets + Toujours inclure les extraits de code Default - Default + Par défaut Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Inclure des extraits de code quand Tab est typé après un identificateur Never add new line on enter - Never add new line on enter + Ne jamais ajouter de nouvelle ligne en appuyant sur Entrée Never include snippets - Never include snippets + Ne jamais inclure d'extrait de code Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Ajouter une nouvelle ligne en appuyant sur Entrée seulement après la fin d'un mot entièrement tapé diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf index f0dfec70e1585..a2043ee6d5bea 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.it.xlf @@ -412,37 +412,37 @@ Mostra elementi da spazi dei nomi non importati (sperimentale); Always add new line on enter - Always add new line on enter + Aggiungi sempre una nuova riga dopo INVIO Always include snippets - Always include snippets + Includi sempre i frammenti Default - Default + Predefinito Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Includi i frammenti quando si digita Tab dopo un identificatore Never add new line on enter - Never add new line on enter + Non aggiungere mai una nuova riga dopo INVIO Never include snippets - Never include snippets + Non includere mai i frammenti Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Aggiungi una nuova riga dopo INVIO solo alla fine della parola digitata diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf index 5b7872afd6bb2..5fd4f43e9b47d 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ja.xlf @@ -412,37 +412,37 @@ Enter キーで常に新しい行を追加する; Always add new line on enter - Always add new line on enter + Enter を押すと常に新しい行を追加します Always include snippets - Always include snippets + 常にスニペットを含める Default - Default + 既定 Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + 識別子の後に Tab を入力したときにスニペットを含める Never add new line on enter - Never add new line on enter + Enter 時に新しい行を追加しません Never include snippets - Never include snippets + スニペットを含めない Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + 単語を完全に入力した後 Enter キーで新しい行のみを追加する diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf index b4a9d605134dc..de61a5007642b 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ko.xlf @@ -412,37 +412,37 @@ Show items from unimported namespaces (experimental); Always add new line on enter - Always add new line on enter + 입력 시 새 줄 항상 추가 Always include snippets - Always include snippets + 코드 조각 항상 포함 Default - Default + 기본값 Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + 식별자 뒤에 Tab을 입력하면 코드 조각 포함 Never add new line on enter - Never add new line on enter + 입력 시 새 줄 추가 안 함 Never include snippets - Never include snippets + 코드 조각 포함 안 함 Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + 단어를 모두 입력하고 <Enter> 키를 누르면 새 줄 추가 diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf index 26706c26c5d64..101df8c706b3c 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pl.xlf @@ -411,37 +411,37 @@ Pokaż elementy z niezaimportowanych przestrzeni nazw (funkcja eksperymentalna); Always add new line on enter - Always add new line on enter + Zawsze dodawaj nowy wiersz po naciśnięciu klawisza Enter Always include snippets - Always include snippets + Zawsze dołączaj fragmenty kodu Default - Default + Domyślne Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Dołącz fragmenty po naciśnięciu klawisza Tab po identyfikatorze Never add new line on enter - Never add new line on enter + Nigdy nie dodawaj nowego wiersza po naciśnięciu klawisza Enter Never include snippets - Never include snippets + Nigdy nie dołączaj fragmentów kodu Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Dodaj nowy wiersz tylko po naciśnięciu klawisza Enter na końcu w pełni wpisanego wyrazu diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf index 1297caa9e09b0..9019773350a57 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.pt-BR.xlf @@ -412,37 +412,37 @@ Mostrar itens de namespaces não importados (experimental); Always add new line on enter - Always add new line on enter + Sempre adicionar uma nova linha ao pressionar enter Always include snippets - Always include snippets + Sempre incluir snippets Default - Default + Padrão Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Incluir snippets de código quando Tab for digitado após um identificador Never add new line on enter - Never add new line on enter + Nunca adicionar uma nova linha ao entrar Never include snippets - Never include snippets + Nunca incluir snippets Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Somente adiciona uma nova linha após digitar toda palavra diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf index e48365faa2edb..d6320c3046efb 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.ru.xlf @@ -412,37 +412,37 @@ Show items from unimported namespaces (experimental); Always add new line on enter - Always add new line on enter + Всегда добавлять новую строку при нажатии клавиши ВВОД Always include snippets - Always include snippets + Всегда включать фрагменты кода Default - Default + По умолчанию Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Включать фрагменты кода, когда после идентификатора нажата клавиша TAB Never add new line on enter - Never add new line on enter + Никогда не добавлять новую строку при нажатии клавиши ВВОД Never include snippets - Never include snippets + Никогда не включать фрагменты кода Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Добавлять только новую строку при нажатии клавиши ВВОД после полностью введенного слова diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf index 4f2b330a697b9..15c065e8b8367 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.tr.xlf @@ -412,37 +412,37 @@ Ad önerilerini göster; Always add new line on enter - Always add new line on enter + Enter'a basıldığında her zaman yeni satır ekle Always include snippets - Always include snippets + Kod parçacıklarını her zaman dahil et Default - Default + Varsayılan Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + Bir tanımlayıcıdan sonra Tab yazılırsa kod parçacıklarını dahil et Never add new line on enter - Never add new line on enter + Enter'a basıldığında hiçbir zaman yeni satır ekleme Never include snippets - Never include snippets + Kod parçacıklarını hiçbir zaman dahil etme Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + Yalnızca tam olarak yazılmış bir kelimeden sonra Enter'a basıldığında yeni satır ekle diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf index 09fdd1b28d195..4af82c645ea0f 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hans.xlf @@ -412,37 +412,37 @@ Show items from unimported namespaces (experimental); Always add new line on enter - Always add new line on enter + 始终在点击回车时时添加新行 Always include snippets - Always include snippets + 始终包含片段 Default - Default + 默认值 Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + 在标识符后键入 Tab 时包含片段 Never add new line on enter - Never add new line on enter + 永远不要在点击回车后添加新行 Never include snippets - Never include snippets + 从不包含片段 Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + 只在键入完整的单词后点击回车后才添加新行 diff --git a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf index 2530541f2ab40..38cdaa0163750 100644 --- a/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf +++ b/src/VisualStudio/CSharp/Impl/xlf/VSPackage.zh-Hant.xlf @@ -412,37 +412,37 @@ Enter 鍵行為; Always add new line on enter - Always add new line on enter + 一律在按 ENTER 時新增新行 Always include snippets - Always include snippets + 一律包含程式碼片段 Default - Default + 預設 Include snippets when Tab is typed after an identifier - Include snippets when Tab is typed after an identifier + 在識別碼後輸入 Tab 時包含程式碼片段 Never add new line on enter - Never add new line on enter + 一律不在按 ENTER 時新增新行 Never include snippets - Never include snippets + 一律不包含程式碼片段 Only add new line on enter after end of fully typed word - Only add new line on enter after end of fully typed word + 只在完整鍵入字的結尾處按 ENTER 來新增新行 diff --git a/src/VisualStudio/Core/Def/AnalyzerDependency/AnalyzerFileWatcherService.cs b/src/VisualStudio/Core/Def/AnalyzerDependency/AnalyzerFileWatcherService.cs index 1aafbe3f230aa..570fe959cf266 100644 --- a/src/VisualStudio/Core/Def/AnalyzerDependency/AnalyzerFileWatcherService.cs +++ b/src/VisualStudio/Core/Def/AnalyzerDependency/AnalyzerFileWatcherService.cs @@ -6,27 +6,19 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation; [Export(typeof(AnalyzerFileWatcherService))] -internal sealed class AnalyzerFileWatcherService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class AnalyzerFileWatcherService(SVsServiceProvider serviceProvider) { - private static readonly object s_analyzerChangedErrorId = new(); - - private readonly VisualStudioWorkspaceImpl _workspace; - private readonly HostDiagnosticUpdateSource _updateSource; - private readonly IVsFileChangeEx _fileChangeService; + private readonly IVsFileChangeEx _fileChangeService = (IVsFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx)); private readonly Dictionary _fileChangeTrackers = new(StringComparer.OrdinalIgnoreCase); @@ -38,37 +30,6 @@ internal sealed class AnalyzerFileWatcherService private readonly object _guard = new(); - private readonly DiagnosticDescriptor _analyzerChangedRule = new( - id: IDEDiagnosticIds.AnalyzerChangedId, - title: ServicesVSResources.AnalyzerChangedOnDisk, - messageFormat: ServicesVSResources.The_analyzer_assembly_0_has_changed_Diagnostics_may_be_incorrect_until_Visual_Studio_is_restarted, - category: FeaturesResources.Roslyn_HostError, - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public AnalyzerFileWatcherService( - VisualStudioWorkspaceImpl workspace, - HostDiagnosticUpdateSource hostDiagnosticUpdateSource, - SVsServiceProvider serviceProvider) - { - _workspace = workspace; - _updateSource = hostDiagnosticUpdateSource; - _fileChangeService = (IVsFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx)); - } - - private void AddAnalyzerChangedWarningArgs(ref TemporaryArray builder, ProjectId projectId, string analyzerPath) - { - var messageArguments = new string[] { analyzerPath }; - - var project = _workspace.CurrentSolution.GetProject(projectId); - if (project != null && DiagnosticData.TryCreate(_analyzerChangedRule, messageArguments, project, out var diagnostic)) - { - _updateSource.UpdateAndAddDiagnosticsArgsForProject(ref builder, projectId, Tuple.Create(s_analyzerChangedErrorId, analyzerPath), SpecializedCollections.SingletonEnumerable(diagnostic)); - } - } - private static DateTime? GetLastUpdateTimeUtc(string fullPath) { try @@ -88,7 +49,7 @@ private void AddAnalyzerChangedWarningArgs(ref TemporaryArray.Empty; - AddAnalyzerChangedWarningArgs(ref argsBuilder.AsRef(), projectId, filePath); - _updateSource.RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - } - // If the the tracker is in place, at this point we can stop checking any further for this assembly if (tracker.PreviousCallToStartFileChangeHasAsynchronouslyCompleted) { @@ -152,20 +106,5 @@ private void Tracker_UpdatedOnDisk(object sender, EventArgs e) tracker.Dispose(); tracker.UpdatedOnDisk -= Tracker_UpdatedOnDisk; - - // Traverse the chain of requesting assemblies to get back to the original analyzer - // assembly. - using var argsBuilder = TemporaryArray.Empty; - foreach (var project in _workspace.CurrentSolution.Projects) - { - var analyzerFileReferences = project.AnalyzerReferences.OfType(); - - if (analyzerFileReferences.Any(a => a.FullPath.Equals(filePath, StringComparison.OrdinalIgnoreCase))) - { - AddAnalyzerChangedWarningArgs(ref argsBuilder.AsRef(), project.Id, filePath); - } - } - - _updateSource.RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); } } diff --git a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs index b86cf9b30a438..ba45c8e817d57 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.WorkspaceEventListener.cs @@ -23,7 +23,7 @@ internal partial class VisualStudioDiagnosticAnalyzerProvider /// Loads VSIX analyzers into workspaces that provide when they are loaded. /// [Export] - [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host, WorkspaceKind.Interactive), Shared] + [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host, WorkspaceKind.Interactive, WorkspaceKind.SemanticSearch), Shared] internal sealed class WorkspaceEventListener : IEventListener { private readonly IAsynchronousOperationListener _listener; diff --git a/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs b/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs index 75131b8050b90..184802ff869d2 100644 --- a/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs +++ b/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs @@ -4,10 +4,9 @@ using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Editor; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioErrorReportingService.cs b/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioErrorReportingService.cs index 1497e07f1042c..9e5a817ba411b 100644 --- a/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioErrorReportingService.cs +++ b/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioErrorReportingService.cs @@ -29,13 +29,16 @@ internal partial class VisualStudioErrorReportingService : IErrorReportingServic public VisualStudioErrorReportingService( IThreadingContext threadingContext, IVsService activityLog, - IAsynchronousOperationListenerProvider listenerProvider, - SVsServiceProvider serviceProvider) + IVsService vsInfoBarUIFactory, + IVsService vsShell, + IAsynchronousOperationListenerProvider listenerProvider) { _threadingContext = threadingContext; _activityLog = activityLog; _listener = listenerProvider.GetListener(FeatureAttribute.Workspace); - _infoBar = new VisualStudioInfoBar(threadingContext, serviceProvider, listenerProvider); + + // Attach this info bar to the global shell location for info-bars (independent of any particular window). + _infoBar = new VisualStudioInfoBar(threadingContext, vsInfoBarUIFactory, vsShell, listenerProvider, windowFrame: null); } public string HostDisplayName => "Visual Studio"; @@ -44,7 +47,7 @@ public void ShowGlobalErrorInfo(string message, TelemetryFeatureName featureName { var stackTrace = exception is null ? "" : GetFormattedExceptionStack(exception); LogGlobalErrorToActivityLog(message, stackTrace); - _infoBar.ShowInfoBar(message, items); + _infoBar.ShowInfoBarMessageFromAnyThread(message, items); Logger.Log(FunctionId.VS_ErrorReportingService_ShowGlobalErrorInfo, KeyValueLogMessage.Create(LogType.UserAction, m => { diff --git a/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioInfoBar.cs b/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioInfoBar.cs index df66d7e859fd6..b138103b89a10 100644 --- a/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioInfoBar.cs +++ b/src/VisualStudio/Core/Def/ErrorReporting/VisualStudioInfoBar.cs @@ -4,119 +4,175 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Imaging; +using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ErrorReporting; -internal sealed class VisualStudioInfoBar +internal sealed class VisualStudioInfoBar( + IThreadingContext threadingContext, + IVsService vsInfoBarUIFactory, + IVsService vsShell, + IAsynchronousOperationListenerProvider listenerProvider, + IVsWindowFrame? windowFrame) { - private readonly IThreadingContext _threadingContext; - private readonly SVsServiceProvider _serviceProvider; - private readonly IAsynchronousOperationListener _listener; + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly IVsService _vsInfoBarUIFactory = vsInfoBarUIFactory; + private readonly IVsService _vsShell = vsShell; + private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.InfoBar); + private readonly IVsWindowFrame? _windowFrame = windowFrame; /// - /// Keep track of the messages that are currently being shown to the user. If we would - /// show the same message again, block that from happening so we don't spam the user with - /// the same message. When the info bar item is dismissed though, we then may show the - /// same message in the future. This is important for user clarity as it's possible for - /// a feature to fail for some reason, then work fine for a while, then fail again. We want - /// the second failure message to be reported to ensure the user is not confused. + /// Keep track of the messages that are currently being shown to the user. If we would show the same message again, + /// block that from happening so we don't spam the user with the same message. When the info bar item is dismissed + /// though, we then may show the same message in the future. This is important for user clarity as it's possible + /// for a feature to fail for some reason, then work fine for a while, then fail again. We want the second failure + /// message to be reported to ensure the user is not confused. /// - /// Accessed on UI thread. + /// Accessed on UI thread only. /// private readonly HashSet _currentlyShowingMessages = []; - public VisualStudioInfoBar( - IThreadingContext threadingContext, - SVsServiceProvider serviceProvider, - IAsynchronousOperationListenerProvider listenerProvider) - { - _threadingContext = threadingContext; - _serviceProvider = serviceProvider; - _listener = listenerProvider.GetListener(FeatureAttribute.InfoBar); - } + public void ShowInfoBarMessageFromAnyThread(string message, params InfoBarUI[] items) + => ShowInfoBarMessageFromAnyThread(message, isCloseButtonVisible: true, KnownMonikers.StatusInformation, items); - public void ShowInfoBar(string message, params InfoBarUI[] items) + public void ShowInfoBarMessageFromAnyThread( + string message, + bool isCloseButtonVisible, + ImageMoniker imageMoniker, + params InfoBarUI[] items) { // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. _threadingContext.JoinableTaskFactory.RunAsync(async () => { - using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBar)); + using var _ = _listener.BeginAsyncOperation(nameof(ShowInfoBarMessageFromAnyThread)); + + await ShowInfoBarMessageAsync(message, isCloseButtonVisible, imageMoniker, items).ConfigureAwait(false); + }); + } + + public async Task ShowInfoBarMessageAsync( + string message, + bool isCloseButtonVisible, + ImageMoniker imageMoniker, + params InfoBarUI[] items) + { + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); + + // If we're already shown this same message to the user, then do not bother showing it + // to them again. It will just be noisy. + if (await GetInfoBarHostObjectAsync().ConfigureAwait(true) is not IVsInfoBarHost infoBarHost) + return null; + + if (_currentlyShowingMessages.Contains(message)) + return null; + + // create action item list + var actionItems = new List(); + + foreach (var item in items) + { + switch (item.Kind) + { + case InfoBarUI.UIKind.Button: + actionItems.Add(new InfoBarButton(item.Title)); + break; + case InfoBarUI.UIKind.HyperLink: + actionItems.Add(new InfoBarHyperlink(item.Title)); + break; + case InfoBarUI.UIKind.Close: + break; + default: + throw ExceptionUtilities.UnexpectedValue(item.Kind); + } + } + + var infoBarModel = new InfoBarModel( + new[] { new InfoBarTextSpan(message) }, + actionItems, + imageMoniker, + isCloseButtonVisible); + + var factory = await _vsInfoBarUIFactory.GetValueAsync().ConfigureAwait(true); + var infoBarUI = factory.CreateInfoBar(infoBarModel); + if (infoBarUI == null) + return null; + + uint? infoBarCookie = null; + var eventSink = new InfoBarEvents(items, onClose: () => + { + Contract.ThrowIfFalse(_threadingContext.JoinableTaskContext.IsOnMainThread); - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); + // Remove the message from the list that we're keeping track of. Future identical + // messages can now be shown. + _currentlyShowingMessages.Remove(message); - // If we're already shown this same message to the user, then do not bother showing it - // to them again. It will just be noisy. - if (!_currentlyShowingMessages.Contains(message) && - _serviceProvider.GetService(typeof(SVsShell)) is IVsShell shell && - ErrorHandler.Succeeded(shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var globalInfoBar)) && - globalInfoBar is IVsInfoBarHost infoBarHost && - _serviceProvider.GetService(typeof(SVsInfoBarUIFactory)) is IVsInfoBarUIFactory factory) + // Run given onClose action if there is one. + items.FirstOrDefault(i => i.Kind == InfoBarUI.UIKind.Close).Action?.Invoke(); + + if (infoBarCookie.HasValue) { - // create action item list - var actionItems = new List(); - - foreach (var item in items) - { - switch (item.Kind) - { - case InfoBarUI.UIKind.Button: - actionItems.Add(new InfoBarButton(item.Title)); - break; - case InfoBarUI.UIKind.HyperLink: - actionItems.Add(new InfoBarHyperlink(item.Title)); - break; - case InfoBarUI.UIKind.Close: - break; - default: - throw ExceptionUtilities.UnexpectedValue(item.Kind); - } - } - - var infoBarModel = new InfoBarModel( - new[] { new InfoBarTextSpan(message) }, - actionItems, - KnownMonikers.StatusInformation, - isCloseButtonVisible: true); - - var infoBarUI = factory.CreateInfoBar(infoBarModel); - if (infoBarUI == null) - return; - - uint? infoBarCookie = null; - var eventSink = new InfoBarEvents(items, onClose: () => - { - Contract.ThrowIfFalse(_threadingContext.JoinableTaskContext.IsOnMainThread); - - // Remove the message from the list that we're keeping track of. Future identical - // messages can now be shown. - _currentlyShowingMessages.Remove(message); - - // Run given onClose action if there is one. - items.FirstOrDefault(i => i.Kind == InfoBarUI.UIKind.Close).Action?.Invoke(); - - if (infoBarCookie.HasValue) - { - infoBarUI.Unadvise(infoBarCookie.Value); - } - }); - - infoBarUI.Advise(eventSink, out var cookie); - infoBarCookie = cookie; - - infoBarHost.AddInfoBar(infoBarUI); - - _currentlyShowingMessages.Add(message); + infoBarUI.Unadvise(infoBarCookie.Value); } }); + + if (ErrorHandler.Succeeded(infoBarUI.Advise(eventSink, out var cookie))) + infoBarCookie = cookie; + + infoBarHost.AddInfoBar(infoBarUI); + + _currentlyShowingMessages.Add(message); + return new InfoBarMessage(this, infoBarHost, message, imageMoniker, infoBarUI, infoBarCookie); + } + + private async Task GetInfoBarHostObjectAsync() + { + _threadingContext.ThrowIfNotOnUIThread(); + + if (_windowFrame != null) + return ErrorHandler.Succeeded(_windowFrame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out var windowInfoBarHostObject)) ? windowInfoBarHostObject : null; + + var shell = await _vsShell.GetValueAsync().ConfigureAwait(true); + return ErrorHandler.Succeeded(shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var globalInfoBarHostObject)) + ? globalInfoBarHostObject + : null; + } + + public sealed class InfoBarMessage( + VisualStudioInfoBar visualStudioInfoBar, + IVsInfoBarHost infoBarHost, + string message, + ImageMoniker imageMoniker, + IVsInfoBarUIElement infoBarUI, + uint? infoBarCookie) + { + public readonly string Message = message; + public readonly ImageMoniker ImageMoniker = imageMoniker; + + private bool _removed; + + public void Remove() + { + visualStudioInfoBar._threadingContext.ThrowIfNotOnUIThread(); + if (_removed) + return; + + _removed = true; + if (infoBarCookie.HasValue) + infoBarUI.Unadvise(infoBarCookie.Value); + + infoBarHost.RemoveInfoBar(infoBarUI); + visualStudioInfoBar._currentlyShowingMessages.Remove(this.Message); + } } private sealed class InfoBarEvents : IVsInfoBarUIEvents diff --git a/src/VisualStudio/Core/Def/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs b/src/VisualStudio/Core/Def/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs index fc948d7dc2b9b..810b88b7b1154 100644 --- a/src/VisualStudio/Core/Def/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/FindReferences/Contexts/WithoutReferencesFindUsagesContext.cs @@ -77,7 +77,11 @@ protected override async ValueTask OnDefinitionFoundWorkerAsync(DefinitionItem d using var _ = ArrayBuilder.GetInstance(out var entries); - if (definition.SourceSpans.Length == 1 && definition.MetadataLocations.IsEmpty) + if (definition.SourceSpans.IsEmpty && definition.MetadataLocations.IsEmpty) + { + entries.Add(new NonNavigableDefinitionItemEntry(this, definitionBucket)); + } + else if (definition.SourceSpans.Length == 1 && definition.MetadataLocations.IsEmpty) { // If we only have a single location, then use the DisplayParts of the // definition as what to show. That way we show enough information for things diff --git a/src/VisualStudio/Core/Def/FindReferences/Entries/NonNavigableEntry.cs b/src/VisualStudio/Core/Def/FindReferences/Entries/NonNavigableEntry.cs new file mode 100644 index 0000000000000..79b0451e5e74b --- /dev/null +++ b/src/VisualStudio/Core/Def/FindReferences/Entries/NonNavigableEntry.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.Collections.Generic; +using System.Windows.Documents; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.VisualStudio.Shell.TableManager; + +namespace Microsoft.VisualStudio.LanguageServices.FindUsages; + +internal partial class StreamingFindUsagesPresenter +{ + private class NonNavigableDefinitionItemEntry(AbstractTableDataSourceFindUsagesContext context, RoslynDefinitionBucket definitionBucket) + : AbstractItemEntry(definitionBucket, context.Presenter) + { + protected override object? GetValueWorker(string keyName) + => keyName switch + { + StandardTableKeyNames.Text => DefinitionBucket.DefinitionItem.DisplayParts.JoinText(), + StandardTableKeyNames.ItemOrigin => ItemOrigin.Exact, + _ => null, + }; + + protected override IList CreateLineTextInlines() + => DefinitionBucket.DefinitionItem.DisplayParts + .ToInlines(Presenter.ClassificationFormatMap, Presenter.TypeMap); + } +} diff --git a/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs b/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs index 256599369a73d..4eb03c294ac1b 100644 --- a/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs +++ b/src/VisualStudio/Core/Def/KeybindingReset/KeybindingResetDetector.cs @@ -90,13 +90,17 @@ internal sealed class KeybindingResetDetector : ForegroundThreadAffinitizedObjec public KeybindingResetDetector( IThreadingContext threadingContext, IGlobalOptionService globalOptions, + IVsService vsInfoBarUIFactory, + IVsService vsShell, SVsServiceProvider serviceProvider, IAsynchronousOperationListenerProvider listenerProvider) : base(threadingContext) { _globalOptions = globalOptions; _serviceProvider = serviceProvider; - _infoBar = new VisualStudioInfoBar(threadingContext, serviceProvider, listenerProvider); + + // Attach this info bar to the global shell location for info-bars (independent of any particular window). + _infoBar = new VisualStudioInfoBar(threadingContext, vsInfoBarUIFactory, vsShell, listenerProvider, windowFrame: null); } public Task InitializeAsync() @@ -239,7 +243,7 @@ private void ShowGoldBar() var message = ServicesVSResources.We_notice_you_suspended_0_Reset_keymappings_to_continue_to_navigate_and_refactor; KeybindingsResetLogger.Log("InfoBarShown"); - _infoBar.ShowInfoBar( + _infoBar.ShowInfoBarMessageFromAnyThread( string.Format(message, ReSharperExtensionName), new InfoBarUI(title: ServicesVSResources.Reset_Visual_Studio_default_keymapping, kind: InfoBarUI.UIKind.Button, diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs index afcb7efb3456c..16af81de4caf5 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage.cs @@ -4,8 +4,11 @@ using System; using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Threading; using Task = System.Threading.Tasks.Task; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; @@ -26,12 +29,25 @@ internal IComponentModel ComponentModel protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(true); + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); _componentModel_doNotAccessDirectly = (IComponentModel)await GetServiceAsync(typeof(SComponentModel)).ConfigureAwait(true); Assumes.Present(_componentModel_doNotAccessDirectly); } + protected override Task OnAfterPackageLoadedAsync(CancellationToken cancellationToken) + { + // TODO: remove, workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1985204 + var globalOptions = ComponentModel.GetService(); + if (globalOptions.GetOption(SemanticSearchFeatureFlag.Enabled)) + { + UIContext.FromUIContextGuid(new Guid(SemanticSearchFeatureFlag.UIContextId)).IsActive = true; + } + + return base.OnAfterPackageLoadedAsync(cancellationToken); + } + protected async Task LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(CancellationToken cancellationToken) { // UIContexts can be "zombied" if UIContexts aren't supported because we're in a command line build or in other scenarios. diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 7e04d4e2f999b..dbe2f9e7d4870 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -164,19 +164,15 @@ - - - - - - - + + + diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 8b5162180b5d2..04342100e7d9e 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -130,6 +130,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_show_outlining_for_declaration_level_constructs", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ShowOutliningForDeclarationLevelConstructs")}, {"csharp_format_on_close_brace", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Close Brace")}, {"dotnet_classify_reassigned_variables", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ClassificationOptions.ClassifyReassignedVariables")}, + {"dotnet_classify_obsolete_symbols", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ClassificationOptions.ClassifyObsoleteSymbols")}, {"dotnet_prefer_system_hash_code", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.PreferSystemHashCode")}, {"visual_studio_color_scheme_name", new RoamingProfileStorage("TextEditor.Roslyn.ColorSchemeName")}, {"visual_studio_color_scheme_use_legacy_enhanced_colors", new RoamingProfileStorage("WindowManagement.Options.UseEnhancedColorsForManagedLanguages")}, @@ -350,6 +351,8 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_collapse_inline_rename_ui", new RoamingProfileStorage("TextEditor.CollapseRenameUI")}, {"dotnet_rename_use_inline_adornment", new RoamingProfileStorage("TextEditor.RenameUseInlineAdornment")}, {"dotnet_preview_inline_rename_changes", new RoamingProfileStorage("TextEditor.Specific.PreviewRename")}, + {"dotnet_collapse_suggestions_in_inline_rename_ui", new RoamingProfileStorage("TextEditor.CollapseRenameSuggestionsUI")}, + {"dotnet_rename_get_suggestions_automatically", new FeatureFlagStorage(@"Editor.AutoSmartRenameSuggestions")}, {"dotnet_rename_asynchronously", new RoamingProfileStorage("TextEditor.Specific.RenameAsynchronously")}, {"dotnet_rename_file", new RoamingProfileStorage("TextEditor.Specific.RenameFile")}, {"dotnet_rename_in_comments", new RoamingProfileStorage("TextEditor.Specific.RenameInComments")}, @@ -363,7 +366,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_format_on_save", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "FormatOnSave")}, {"dotnet_enable_full_solution_analysis_memory_monitor", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Full Solution Analysis Memory Monitor")}, {"dotnet_code_analysis_in_separate_process", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "OOP64Bit")}, - {"dotnet_enable_core_clr_in_code_analysis_process", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "OOPCoreClr")}, {"dotnet_enable_server_garbage_collection_in_code_analysis_process", new FeatureFlagStorage(@"Roslyn.OOPServerGC")}, {"dotnet_remove_intellicode_recommendation_limit", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "RemoveRecommendationLimit")}, {"dotnet_enable_rename_tracking", new LocalUserProfileStorage(@"Roslyn\Internal\OnOff\Features", "Rename Tracking")}, @@ -377,7 +379,8 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_detect_and_offer_editor_features_for_probable_json_strings", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.DetectAndOfferEditorFeaturesForProbableJsonStrings")}, {"dotnet_highlight_related_json_components", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.HighlightRelatedJsonComponentsUnderCursor")}, {"dotnet_report_invalid_json_patterns", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ReportInvalidJsonPatterns")}, - {"visual_studio_enable_key_binding_reset", new FeatureFlagStorage(@"Roslyn.KeybindingResetEnabled")}, + {"visual_studio_enable_key_binding_reset", new FeatureFlagStorage("Roslyn.KeybindingResetEnabled")}, + {"visual_studio_enable_semantic_search", new FeatureFlagStorage("Roslyn.SemanticSearchEnabled")}, {"visual_studio_key_binding_needs_reset", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "NeedsReset")}, {"visual_studio_key_binding_reset_never_show_again", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "NeverShowAgain")}, {"visual_studio_resharper_key_binding_status", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "ReSharperStatus")}, @@ -426,7 +429,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"visual_basic_style_unused_value_assignment_preference", new RoamingProfileStorage("TextEditor.VisualBasic.Specific.UnusedValueAssignmentPreference")}, {"visual_basic_style_unused_value_expression_statement_preference", new RoamingProfileStorage("TextEditor.VisualBasic.Specific.UnusedValueExpressionStatementPreference")}, {"visual_studio_navigate_to_object_browser", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.NavigateToObjectBrowser")}, - {"visual_studio_workspace_partial_load_mode", new FeatureFlagStorage(@"Roslyn.PartialLoadMode")}, {"dotnet_disable_recoverable_text", new FeatureFlagStorage(@"Roslyn.DisableRecoverableText")}, {"dotnet_validate_compilation_tracker_states", new FeatureFlagStorage(@"Roslyn.ValidateCompilationTrackerStates")}, {"dotnet_enable_diagnostics_in_source_generated_files", new RoamingProfileStorage("TextEditor.Roslyn.Specific.EnableDiagnosticsInSourceGeneratedFilesExperiment")}, diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs index c27d99a8f9a06..12b23cdc6f93e 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioSettingsOptionPersister.cs @@ -109,7 +109,20 @@ public bool TryFetch(OptionKey2 optionKey, string storageKey, out object? value) var underlyingType = Nullable.GetUnderlyingType(storageType); if (underlyingType?.IsEnum == true) - return manager.TryGetValue(storageKey, out int? value) == GetValueResult.Success ? (value.HasValue ? Enum.ToObject(underlyingType, value.Value) : null) : default(Optional); + { + if (manager.TryGetValue(storageKey, out int? nullableValue) == GetValueResult.Success) + { + return nullableValue.HasValue ? Enum.ToObject(underlyingType, nullableValue.Value) : null; + } + else if (manager.TryGetValue(storageKey, out int value) == GetValueResult.Success) + { + return Enum.ToObject(underlyingType, value); + } + else + { + return default; + } + } if (storageType == typeof(NamingStylePreferences)) { diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index 63948c018ba84..b8fe5b8a55ddf 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs index 888fdd8541c64..aa2ce89542771 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectFactory.cs @@ -33,7 +33,6 @@ internal sealed class VisualStudioProjectFactory : IVsTypeScriptVisualStudioProj private readonly IThreadingContext _threadingContext; private readonly VisualStudioWorkspaceImpl _visualStudioWorkspaceImpl; private readonly ImmutableArray> _dynamicFileInfoProviders; - private readonly HostDiagnosticUpdateSource _hostDiagnosticUpdateSource; private readonly IVisualStudioDiagnosticAnalyzerProviderFactory _vsixAnalyzerProviderFactory; private readonly IVsService _solution2; @@ -43,14 +42,12 @@ public VisualStudioProjectFactory( IThreadingContext threadingContext, VisualStudioWorkspaceImpl visualStudioWorkspaceImpl, [ImportMany] IEnumerable> fileInfoProviders, - HostDiagnosticUpdateSource hostDiagnosticUpdateSource, IVisualStudioDiagnosticAnalyzerProviderFactory vsixAnalyzerProviderFactory, IVsService solution2) { _threadingContext = threadingContext; _visualStudioWorkspaceImpl = visualStudioWorkspaceImpl; _dynamicFileInfoProviders = fileInfoProviders.AsImmutableOrEmpty(); - _hostDiagnosticUpdateSource = hostDiagnosticUpdateSource; _vsixAnalyzerProviderFactory = vsixAnalyzerProviderFactory; _solution2 = solution2; } @@ -96,7 +93,7 @@ public async Task CreateAndAddToWorkspaceAsync( _visualStudioWorkspaceImpl.ProjectSystemProjectFactory.SolutionPath = solutionFilePath; _visualStudioWorkspaceImpl.ProjectSystemProjectFactory.SolutionTelemetryId = GetSolutionSessionId(); - var hostInfo = new ProjectSystemHostInfo(_dynamicFileInfoProviders, _hostDiagnosticUpdateSource, vsixAnalyzerProvider); + var hostInfo = new ProjectSystemHostInfo(_dynamicFileInfoProviders, HostDiagnosticUpdateSource.Instance, vsixAnalyzerProvider); var project = await _visualStudioWorkspaceImpl.ProjectSystemProjectFactory.CreateAndAddToWorkspaceAsync(projectSystemName, language, creationInfo, hostInfo); _visualStudioWorkspaceImpl.AddProjectToInternalMaps(project, creationInfo.Hierarchy, creationInfo.ProjectGuid, projectSystemName); diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index 9f3d95eaca706..051a66a017377 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -15,6 +15,7 @@ using EnvDTE; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; @@ -135,7 +136,6 @@ public VisualStudioWorkspaceImpl(ExportProvider exportProvider, IAsyncServicePro new ExternalErrorDiagnosticUpdateSource( this, exportProvider.GetExportedValue(), - exportProvider.GetExportedValue(), exportProvider.GetExportedValue(), exportProvider.GetExportedValue(), _threadingContext), diff --git a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs index 6aec52668082b..8185783e7f170 100644 --- a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs @@ -120,8 +120,7 @@ private VisualStudioRemoteHostClientProvider( var serviceBroker = brokeredServiceContainer.GetFullAccessServiceBroker(); var configuration = - (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPCoreClr) ? RemoteProcessConfiguration.Core : 0) | - (_globalOptions.GetOption(RemoteHostOptionsStorage.OOPServerGCFeatureFlag) ? RemoteProcessConfiguration.ServerGC : 0); + _globalOptions.GetOption(RemoteHostOptionsStorage.OOPServerGCFeatureFlag) ? RemoteProcessConfiguration.ServerGC : 0; // VS AsyncLazy does not currently support cancellation: var client = await ServiceHubRemoteHostClient.CreateAsync(Services, configuration, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 9f14f40d824f8..79a33a370c61e 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ColorSchemes; using Microsoft.CodeAnalysis.Common; +using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; @@ -179,6 +180,11 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke serviceBrokerContainer.Proffer( WorkspaceProjectFactoryServiceDescriptor.ServiceDescriptor, (_, _, _, _) => ValueTaskFactory.FromResult(new WorkspaceProjectFactoryService(this.ComponentModel.GetService()))); + + // Must be profferred before any C#/VB projects are loaded and the corresponding UI context activated. + serviceBrokerContainer.Proffer( + ManagedHotReloadLanguageServiceDescriptor.Descriptor, + (_, _, _, _) => ValueTaskFactory.FromResult(new ManagedEditAndContinueLanguageServiceBridge(this.ComponentModel.GetService()))); } private async Task LoadOptionPersistersAsync(IComponentModel componentModel, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs b/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs new file mode 100644 index 0000000000000..4b5e4cadb7d7d --- /dev/null +++ b/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.VisualStudio.LanguageServices; + +internal static class SemanticSearchFeatureFlag +{ + public static readonly Option2 Enabled = new("visual_studio_enable_semantic_search", defaultValue: false); + + /// + /// Context id that indicates that Semantic Search feature is enabled. + /// TODO: remove, workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1985204 + /// + public const string UIContextId = "D5801818-6009-40BE-9204-8897C23D2856"; +} diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index aca53283fce6f..a0c8bf8c7a3ee 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -859,6 +859,12 @@ Additional information: {1} Search found no results + + Search found no results: no C# or Visual Basic projects are opened + + + Search canceled + Sync Class View @@ -1382,8 +1388,8 @@ Additional information: {1} Extract Base Class - - This file is auto-generated by the generator '{0}' and cannot be edited. + + This file was generated by '{0}' at {1} and cannot be edited. [generated by {0}] @@ -1672,9 +1678,6 @@ Additional information: {1} Run code analysis in separate process (requires restart) - - Run code analysis on latest .NET (requires restart) - Quick Actions @@ -1894,7 +1897,22 @@ Additional information: {1} When types loosely match + + Strike out obsolete symbols + {0} ({1}) + + The project no longer exists + + + Show guides for comments and preprocessor regions + + + Semantic Search Results + + + Semantic Search ({0}) + \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs index f627bc807b105..efcbbc6b94d69 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs @@ -214,14 +214,9 @@ await _editHandlerService.ApplyAsync( scope.GetCodeAnalysisProgress(), context.UserCancellationToken).ConfigureAwait(false); + // Kick off diagnostic re-analysis for affected document so that the configured diagnostic gets refreshed. if (selectedDiagnostic.DocumentId != null) - { - // Kick off diagnostic re-analysis for affected document so that the configured diagnostic gets refreshed. - _ = Task.Run(() => - { - _diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(selectedDiagnostic.DocumentId), highPriority: true); - }); - } + _diagnosticService.RequestDiagnosticRefresh(); } catch (OperationCanceledException) { diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 62701ab03fb37..fe87d2561bea2 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -413,11 +413,7 @@ await _editHandlerService.ApplyAsync( cancellationToken).ConfigureAwait(false); // Kick off diagnostic re-analysis for affected projects so that diagnostics gets refreshed. - _ = Task.Run(() => - { - var reanalyzeDocuments = diagnosticsToFix.Select(d => d.DocumentId).WhereNotNull().Distinct(); - _diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: reanalyzeDocuments, highPriority: true); - }); + _diagnosticService.RequestDiagnosticRefresh(); } } catch (OperationCanceledException) diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index 4510c3f95c160..7569c1f5f4204 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -36,7 +36,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; /// It raises events about diagnostic updates, which eventually trigger the "Build + Intellisense" and "Build only" error list diagnostic /// sources to update the reported diagnostics. /// -internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSource, IDisposable +internal sealed class ExternalErrorDiagnosticUpdateSource : IDisposable { private readonly Workspace _workspace; private readonly IDiagnosticAnalyzerService _diagnosticService; @@ -51,16 +51,6 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou /// private readonly TaskQueue _taskQueue; - /// - /// Task queue to serialize all the post-build and post error list refresh tasks. - /// Error list refresh requires build/live diagnostics de-duping to complete, which happens during - /// . - /// Computationally expensive tasks such as writing build errors into persistent storage, - /// invoking background analysis on open files/solution after build completes, etc. - /// are added to this task queue to help ensure faster error list refresh. - /// - private readonly TaskQueue _postBuildAndErrorListRefreshTaskQueue; - // Gate for concurrent access and fields guarded with this gate. private readonly object _gate = new(); private InProgressState? _stateDoNotAccessDirectly; @@ -76,13 +66,11 @@ internal sealed class ExternalErrorDiagnosticUpdateSource : IDiagnosticUpdateSou public ExternalErrorDiagnosticUpdateSource( VisualStudioWorkspace workspace, IDiagnosticAnalyzerService diagnosticService, - IDiagnosticUpdateSourceRegistrationService registrationService, IGlobalOperationNotificationService notificationService, IAsynchronousOperationListenerProvider listenerProvider, IThreadingContext threadingContext) : this(workspace, diagnosticService, notificationService, listenerProvider.GetListener(FeatureAttribute.ErrorList), threadingContext.DisposalToken) { - registrationService.Register(this); } /// @@ -97,7 +85,6 @@ internal ExternalErrorDiagnosticUpdateSource( { // use queue to serialize work. no lock needed _taskQueue = new TaskQueue(listener, TaskScheduler.Default); - _postBuildAndErrorListRefreshTaskQueue = new TaskQueue(listener, TaskScheduler.Default); _disposalToken = disposalToken; _workspace = workspace; @@ -123,12 +110,6 @@ internal ExternalErrorDiagnosticUpdateSource( /// public event EventHandler>? DiagnosticsUpdated; - /// - /// Event generated from the serialized whenever build-only diagnostics are cleared during a build in Visual Studio. - /// These diagnostics are not supported from intellisense and only get refreshed during actual build. - /// - public event EventHandler DiagnosticsCleared { add { } remove { } } - /// /// Indicates if a build is currently in progress inside Visual Studio. /// @@ -175,13 +156,13 @@ public void ClearErrors(ProjectId projectId) // Update the state to clear diagnostics and raise corresponding diagnostic updated events // on a serialized task queue. - _taskQueue.ScheduleTask(nameof(ClearErrors), async () => + _taskQueue.ScheduleTask(nameof(ClearErrors), () => { if (state == null) { // TODO: Is it possible that ClearErrors can be invoked while the build is not in progress? // We fallback to current solution in the workspace and clear errors for the project. - await ClearErrorsCoreAsync(projectId, _workspace.CurrentSolution, state).ConfigureAwait(false); + ClearErrorsCore(projectId, _workspace.CurrentSolution, state); } else { @@ -193,12 +174,12 @@ public void ClearErrors(ProjectId projectId) // If so, we don't need to clear diagnostics for it again. if (state.WereProjectErrorsCleared(projectId)) { - return; + return Task.CompletedTask; } var solution = state.Solution; - await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); + ClearErrorsCore(projectId, solution, state); var transitiveProjectIds = solution.GetProjectDependencyGraph().GetProjectsThatTransitivelyDependOnThisProject(projectId); foreach (var projectId in transitiveProjectIds) @@ -208,14 +189,16 @@ public void ClearErrors(ProjectId projectId) continue; } - await ClearErrorsCoreAsync(projectId, solution, state).ConfigureAwait(false); + ClearErrorsCore(projectId, solution, state); } } + + return Task.CompletedTask; }, GetApplicableCancellationToken(state)); return; - async ValueTask ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InProgressState? state) + void ClearErrorsCore(ProjectId projectId, Solution solution, InProgressState? state) { Debug.Assert(state == null || !state.WereProjectErrorsCleared(projectId)); @@ -231,8 +214,6 @@ async ValueTask ClearErrorsCoreAsync(ProjectId projectId, Solution solution, InP ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); } - await SetLiveErrorsForProjectAsync(projectId, ImmutableArray.Empty, GetApplicableCancellationToken(state)).ConfigureAwait(false); - state?.MarkErrorsCleared(projectId); OnBuildProgressChanged(state, BuildProgress.Updated); @@ -355,32 +336,26 @@ internal void OnSolutionBuildCompleted() var inProgressState = ClearInProgressState(); // Enqueue build/live sync in the queue. - _taskQueue.ScheduleTask("OnSolutionBuild", async () => + _taskQueue.ScheduleTask("OnSolutionBuild", () => { - try + // nothing to do + if (inProgressState == null) { - // nothing to do - if (inProgressState == null) - { - return; - } + return Task.CompletedTask; + } - // Mark the status as updated to refresh error list before we invoke 'SyncBuildErrorsAndReportAsync', which can take some time to complete. - OnBuildProgressChanged(inProgressState, BuildProgress.Updated); + // Mark the status as updated to refresh error list before we invoke 'SyncBuildErrorsAndReportAsync', which can take some time to complete. + OnBuildProgressChanged(inProgressState, BuildProgress.Updated); - // We are about to update live analyzer data using one from build. - // pause live analyzer - using var operation = _notificationService.Start("BuildDone"); - if (_diagnosticService is DiagnosticAnalyzerService diagnosticService) - await SyncBuildErrorsAndReportOnBuildCompletedAsync(diagnosticService, inProgressState).ConfigureAwait(false); + // We are about to update live analyzer data using one from build. + // pause live analyzer + using var operation = _notificationService.Start("BuildDone"); + if (_diagnosticService is DiagnosticAnalyzerService) + SyncBuildErrorsAndReportOnBuildCompleted(inProgressState); - // Mark build as complete. - OnBuildProgressChanged(inProgressState, BuildProgress.Done); - } - finally - { - await _postBuildAndErrorListRefreshTaskQueue.LastScheduledTask.ConfigureAwait(false); - } + // Mark build as complete. + OnBuildProgressChanged(inProgressState, BuildProgress.Done); + return Task.CompletedTask; }, GetApplicableCancellationToken(inProgressState)); } @@ -389,11 +364,11 @@ internal void OnSolutionBuildCompleted() /// It raises diagnostic update events for both the Build-only diagnostics and Build + Intellisense diagnostics /// in the error list. /// - private ValueTask SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnalyzerService diagnosticService, InProgressState inProgressState) + private void SyncBuildErrorsAndReportOnBuildCompleted(InProgressState inProgressState) { var solution = inProgressState.Solution; var cancellationToken = inProgressState.CancellationToken; - var (allLiveErrors, pendingLiveErrorsToSync) = inProgressState.GetLiveErrors(); + var allLiveErrors = inProgressState.GetLiveErrors(); // Raise events for build only errors using var argsBuilder = TemporaryArray.Empty; @@ -417,27 +392,24 @@ private ValueTask SyncBuildErrorsAndReportOnBuildCompletedAsync(DiagnosticAnalyz } ProcessAndRaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - - // Report pending live errors - return diagnosticService.SynchronizeWithBuildAsync(_workspace, pendingLiveErrorsToSync, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: true, cancellationToken); } - private DiagnosticsUpdatedArgs CreateArgsToReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) + private static DiagnosticsUpdatedArgs CreateArgsToReportBuildErrors(T item, Solution solution, ImmutableArray buildErrors) { if (item is ProjectId projectId) { - return CreateDiagnosticsCreatedArgs(projectId, solution, projectId, documentId: null, buildErrors); + return CreateDiagnosticsCreatedArgs(solution, projectId, documentId: null, buildErrors); } RoslynDebug.Assert(item is DocumentId); var documentId = (DocumentId)(object)item; - return CreateDiagnosticsCreatedArgs(documentId, solution, documentId.ProjectId, documentId, buildErrors); + return CreateDiagnosticsCreatedArgs(solution, documentId.ProjectId, documentId, buildErrors); } - private void AddArgsToClearBuildOnlyProjectErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId) + private static void AddArgsToClearBuildOnlyProjectErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId) { // Remove all project errors - builder.Add(CreateDiagnosticsRemovedArgs(projectId, solution, projectId, documentId: null)); + builder.Add(CreateDiagnosticsRemovedArgs(solution, projectId, documentId: null)); var project = solution.GetProject(projectId); if (project == null) @@ -452,8 +424,8 @@ private void AddArgsToClearBuildOnlyProjectErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId, DocumentId? documentId) - => builder.Add(CreateDiagnosticsRemovedArgs(documentId, solution, projectId, documentId)); + private static void AddArgsToClearBuildOnlyDocumentErrors(ref TemporaryArray builder, Solution solution, ProjectId? projectId, DocumentId? documentId) + => builder.Add(CreateDiagnosticsRemovedArgs(solution, projectId, documentId)); public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic) { @@ -462,10 +434,10 @@ public void AddNewErrors(ProjectId projectId, DiagnosticData diagnostic) // Capture state that will be processed in background thread. var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Project New Errors", async () => + _taskQueue.ScheduleTask("Project New Errors", () => { - await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); state.AddError(projectId, diagnostic); + return Task.CompletedTask; }, state.CancellationToken); } @@ -476,10 +448,10 @@ public void AddNewErrors(DocumentId documentId, DiagnosticData diagnostic) // Capture state that will be processed in background thread. var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Document New Errors", async () => + _taskQueue.ScheduleTask("Document New Errors", () => { - await ReportPreviousProjectErrorsIfRequiredAsync(documentId.ProjectId, state).ConfigureAwait(false); state.AddError(documentId, diagnostic); + return Task.CompletedTask; }, state.CancellationToken); } @@ -492,55 +464,16 @@ public void AddNewErrors( // Capture state that will be processed in background thread var state = GetOrCreateInProgressState(); - _taskQueue.ScheduleTask("Project New Errors", async () => + _taskQueue.ScheduleTask("Project New Errors", () => { - await ReportPreviousProjectErrorsIfRequiredAsync(projectId, state).ConfigureAwait(false); - foreach (var kv in documentErrorMap) state.AddErrors(kv.Key, kv.Value); state.AddErrors(projectId, projectErrors); + return Task.CompletedTask; }, state.CancellationToken); } - /// - /// This method is invoked from all overloads before it adds the new errors to the in progress state. - /// It checks if build reported errors for a different project then the previous callback to report errors. - /// This provides a good checkpoint to de-dupe build and live errors for lastProjectId and - /// raise diagnostic updated events for that project. - /// This ensures that error list keeps getting refreshed while a build is in progress, as opposed to doing all the work - /// and a single refresh when the build completes. - /// - private ValueTask ReportPreviousProjectErrorsIfRequiredAsync(ProjectId projectId, InProgressState state) - { - if (state.TryGetLastProjectWithReportedErrors() is ProjectId lastProjectId && - lastProjectId != projectId) - { - return SetLiveErrorsForProjectAsync(lastProjectId, state); - } - - return default; - } - - private async ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, InProgressState state) - { - var diagnostics = state.GetLiveErrorsForProject(projectId); - await SetLiveErrorsForProjectAsync(projectId, diagnostics, state.CancellationToken).ConfigureAwait(false); - state.MarkLiveErrorsReported(projectId); - } - - private ValueTask SetLiveErrorsForProjectAsync(ProjectId projectId, ImmutableArray diagnostics, CancellationToken cancellationToken) - { - if (_diagnosticService is DiagnosticAnalyzerService diagnosticAnalyzerService) - { - // make those errors live errors - var map = ProjectErrorMap.Empty.Add(projectId, diagnostics); - return diagnosticAnalyzerService.SynchronizeWithBuildAsync(_workspace, map, _postBuildAndErrorListRefreshTaskQueue, onBuildCompleted: false, cancellationToken); - } - - return default; - } - private CancellationToken GetApplicableCancellationToken(InProgressState? state) => state?.CancellationToken ?? _disposalToken; @@ -580,14 +513,14 @@ private InProgressState GetOrCreateInProgressState() } } - private DiagnosticsUpdatedArgs CreateDiagnosticsCreatedArgs(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray items) + private static DiagnosticsUpdatedArgs CreateDiagnosticsCreatedArgs(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableArray items) { - return DiagnosticsUpdatedArgs.DiagnosticsCreated(CreateArgumentKey(id), _workspace, solution, projectId, documentId, items); + return DiagnosticsUpdatedArgs.DiagnosticsCreated(solution, projectId, documentId, items); } - private DiagnosticsUpdatedArgs CreateDiagnosticsRemovedArgs(object? id, Solution solution, ProjectId? projectId, DocumentId? documentId) + private static DiagnosticsUpdatedArgs CreateDiagnosticsRemovedArgs(Solution solution, ProjectId? projectId, DocumentId? documentId) { - return DiagnosticsUpdatedArgs.DiagnosticsRemoved(CreateArgumentKey(id), _workspace, solution, projectId, documentId); + return DiagnosticsUpdatedArgs.DiagnosticsRemoved(solution, projectId, documentId); } private void ProcessAndRaiseDiagnosticsUpdated(ImmutableArray argsCollection) @@ -614,8 +547,6 @@ private void ProcessAndRaiseDiagnosticsUpdated(ImmutableArray new(id); - private void RaiseBuildProgressChanged(BuildProgress progress) => BuildProgressChanged?.Invoke(this, progress); @@ -684,22 +615,12 @@ private sealed class InProgressState /// private readonly HashSet _projectsWithErrorsCleared = []; - /// - /// Set of projects for which we have reported all intellisense/live diagnostics. - /// - private readonly HashSet _projectsWithAllLiveErrorsReported = []; - /// /// Set of projects which have at least one project or document diagnostic in /// and/or . /// private readonly HashSet _projectsWithErrors = []; - /// - /// Last project for which build reported an error through one of the methods. - /// - private ProjectId? _lastProjectWithReportedErrors; - /// /// Counter to help order the diagnostics in error list based on the order in which they were reported during build. /// @@ -721,7 +642,7 @@ public InProgressState(ExternalErrorDiagnosticUpdateSource owner, Solution solut private static ImmutableHashSet GetOrCreateDiagnosticIds( ProjectId projectId, Dictionary> diagnosticIdMap, - Func> computeDiagosticIds) + Func> computeDiagnosticIds) { lock (diagnosticIdMap) { @@ -731,7 +652,7 @@ private static ImmutableHashSet GetOrCreateDiagnosticIds( } } - var computedIds = computeDiagosticIds(); + var computedIds = computeDiagnosticIds(); lock (diagnosticIdMap) { @@ -781,30 +702,18 @@ public void MarkErrorsCleared(ProjectId projectId) public bool WereProjectErrorsCleared(ProjectId projectId) => _projectsWithErrorsCleared.Contains(projectId); - public void MarkLiveErrorsReported(ProjectId projectId) - => _projectsWithAllLiveErrorsReported.Add(projectId); - - public ProjectId? TryGetLastProjectWithReportedErrors() - => _lastProjectWithReportedErrors; - - public (ImmutableArray allLiveErrors, ProjectErrorMap pendingLiveErrorsToSync) GetLiveErrors() + public ImmutableArray GetLiveErrors() { var allLiveErrorsBuilder = ImmutableArray.CreateBuilder(); - var pendingLiveErrorsToSyncBuilder = ImmutableDictionary.CreateBuilder>(); foreach (var projectId in GetProjectsWithErrors()) { CancellationToken.ThrowIfCancellationRequested(); var errors = GetLiveErrorsForProject(projectId); allLiveErrorsBuilder.AddRange(errors); - - if (!_projectsWithAllLiveErrorsReported.Contains(projectId)) - { - pendingLiveErrorsToSyncBuilder.Add(projectId, errors); - } } - return (allLiveErrorsBuilder.ToImmutable(), pendingLiveErrorsToSyncBuilder.ToImmutable()); + return allLiveErrorsBuilder.ToImmutable(); // Local functions. IEnumerable GetProjectsWithErrors() @@ -983,17 +892,9 @@ void RecordProjectContainsErrors() RoslynDebug.Assert(key is DocumentId or ProjectId); var projectId = (key is DocumentId documentId) ? documentId.ProjectId : (ProjectId)(object)key; - // New errors reported for project, need to refresh live errors. - _projectsWithAllLiveErrorsReported.Remove(projectId); - if (!_projectsWithErrors.Add(projectId)) - { return; - } - // this will make build only error list to be updated per project rather than per solution. - // basically this will make errors up to last project to show up in error list - _lastProjectWithReportedErrors = projectId; _owner.OnBuildProgressChanged(this, BuildProgress.Updated); } } @@ -1003,25 +904,6 @@ private static Dictionary GetErrorSet(Dictionary map.GetOrAdd(key, _ => new Dictionary(DiagnosticDataComparer.Instance)); } - private sealed class ArgumentKey : BuildToolId.Base - { - public ArgumentKey(object? key) : base(key) - { - } - - public override string BuildTool - { - get { return PredefinedBuildTools.Build; } - } - - public override bool Equals(object? obj) - => obj is ArgumentKey && - base.Equals(obj); - - public override int GetHashCode() - => base.GetHashCode(); - } - private sealed class DiagnosticDataComparer : IEqualityComparer { public static readonly DiagnosticDataComparer Instance = new(); diff --git a/src/VisualStudio/Core/Def/TaskList/HostDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/HostDiagnosticUpdateSource.cs index 5cac4a34d944e..3f20d78d8cc85 100644 --- a/src/VisualStudio/Core/Def/TaskList/HostDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/HostDiagnosticUpdateSource.cs @@ -2,147 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using System; -using System.Collections.Generic; -using System.ComponentModel.Composition; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; -// exporting both Abstract and HostDiagnosticUpdateSource is just to make testing easier. -// use HostDiagnosticUpdateSource when abstract one is not needed for testing purpose -[Export(typeof(AbstractHostDiagnosticUpdateSource))] -[Export(typeof(HostDiagnosticUpdateSource))] -internal sealed class HostDiagnosticUpdateSource : AbstractHostDiagnosticUpdateSource, IProjectSystemDiagnosticSource +internal sealed class HostDiagnosticUpdateSource : IProjectSystemDiagnosticSource { - private readonly Lazy _workspace; - - private readonly object _gate = new(); - private readonly Dictionary> _diagnosticMap = []; - - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public HostDiagnosticUpdateSource(Lazy workspace, IDiagnosticUpdateSourceRegistrationService registrationService) - { - _workspace = workspace; - - registrationService.Register(this); - } - - public override Workspace Workspace - { - get - { - return _workspace.Value; - } - } - - private void AddDiagnosticsCreatedArgsForProject(ref TemporaryArray builder, ProjectId projectId, object key, IEnumerable items) - { - var args = DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(projectId, key), - Workspace, - solution: null, - projectId: projectId, - documentId: null, - diagnostics: items.AsImmutableOrEmpty()); - - builder.Add(args); - } - - private void AddDiagnosticsRemovedArgsForProject(ref TemporaryArray builder, ProjectId projectId, object key) - { - var args = DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(projectId, key), - Workspace, - solution: null, - projectId: projectId, - documentId: null); - - builder.Add(args); - } - - private object CreateId(ProjectId projectId, object key) => Tuple.Create(this, projectId, key); - - public void UpdateAndAddDiagnosticsArgsForProject(ref TemporaryArray builder, ProjectId projectId, object key, IEnumerable items) - { - Contract.ThrowIfNull(projectId); - Contract.ThrowIfNull(key); - Contract.ThrowIfNull(items); - - lock (_gate) - { - _diagnosticMap.GetOrAdd(projectId, id => new HashSet()).Add(key); - } - - AddDiagnosticsCreatedArgsForProject(ref builder, projectId, key, items); - } - - void IProjectSystemDiagnosticSource.UpdateDiagnosticsForProject(ProjectId projectId, object key, IEnumerable items) - { - using var argsBuilder = TemporaryArray.Empty; - UpdateAndAddDiagnosticsArgsForProject(ref argsBuilder.AsRef(), projectId, key, items); - RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - } - - void IProjectSystemDiagnosticSource.ClearAllDiagnosticsForProject(ProjectId projectId) - { - Contract.ThrowIfNull(projectId); - - HashSet projectDiagnosticKeys; - lock (_gate) - { - if (_diagnosticMap.TryGetValue(projectId, out projectDiagnosticKeys)) - { - _diagnosticMap.Remove(projectId); - } - } - - using var argsBuilder = TemporaryArray.Empty; - if (projectDiagnosticKeys != null) - { - foreach (var key in projectDiagnosticKeys) - { - AddDiagnosticsRemovedArgsForProject(ref argsBuilder.AsRef(), projectId, key); - } - } - - AddArgsToClearAnalyzerDiagnostics(ref argsBuilder.AsRef(), projectId); - RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); - } - - internal void ClearAndAddDiagnosticsArgsForProject(ref TemporaryArray builder, ProjectId projectId, object key) - { - Contract.ThrowIfNull(projectId); - Contract.ThrowIfNull(key); - - var raiseEvent = false; - lock (_gate) - { - if (_diagnosticMap.TryGetValue(projectId, out var projectDiagnosticKeys)) - { - raiseEvent = projectDiagnosticKeys.Remove(key); - } - } - - if (raiseEvent) - { - AddDiagnosticsRemovedArgsForProject(ref builder, projectId, key); - } - } + public static readonly HostDiagnosticUpdateSource Instance = new(); - void IProjectSystemDiagnosticSource.ClearDiagnosticsForProject(ProjectId projectId, object key) + private HostDiagnosticUpdateSource() { - using var argsBuilder = TemporaryArray.Empty; - ClearAndAddDiagnosticsArgsForProject(ref argsBuilder.AsRef(), projectId, key); - RaiseDiagnosticsUpdated(argsBuilder.ToImmutableAndClear()); } public DiagnosticData CreateAnalyzerLoadFailureDiagnostic(AnalyzerLoadFailureEventArgs e, string fullPath, ProjectId projectId, string language) diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index dbb65c0e55adf..103ffe86eaa94 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs @@ -171,7 +171,7 @@ private void OnDataBufferChanged(object sender, TextContentChangedEventArgs e) { // we don't actually care what has changed in primary buffer. we just want to re-analyze secondary buffer // when primary buffer has changed to update diagnostic positions. - _diagnosticAnalyzerService.Reanalyze(this.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(this.ContainedDocument.Id), highPriority: false); + _diagnosticAnalyzerService.RequestDiagnosticRefresh(); } public string GetFilePathFromBuffers() diff --git a/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs index f02492da4e0da..fbdf6d49b5e8e 100644 --- a/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Workspace/SourceGeneratedFileManager.cs @@ -10,7 +10,9 @@ 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.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -30,19 +32,24 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation; +using InfoBarInfo = (ImageMoniker imageMoniker, string message); + /// /// Provides the support for opening files pointing to source generated documents, and keeping the content updated accordingly. /// [Export(typeof(SourceGeneratedFileManager))] internal sealed class SourceGeneratedFileManager : IOpenTextBufferEventListener { - private readonly IServiceProvider _serviceProvider; + private readonly SVsServiceProvider _serviceProvider; + private readonly IVsService _vsInfoBarUIFactory; + private readonly IVsService _vsShell; private readonly IThreadingContext _threadingContext; - private readonly ForegroundThreadAffinitizedObject _foregroundThreadAffinitizedObject; - private readonly IAsynchronousOperationListener _listener; private readonly ITextDocumentFactoryService _textDocumentFactoryService; private readonly VisualStudioDocumentNavigationService _visualStudioDocumentNavigationService; + private readonly IAsynchronousOperationListener _listener; + private readonly IAsynchronousOperationListenerProvider _listenerProvider; + /// /// The temporary directory that we'll create file names under to act as a prefix we can later recognize and use. /// @@ -67,7 +74,9 @@ internal sealed class SourceGeneratedFileManager : IOpenTextBufferEventListener [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public SourceGeneratedFileManager( - [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, + SVsServiceProvider serviceProvider, + IVsService vsInfoBarUIFactory, + IVsService vsShell, IThreadingContext threadingContext, OpenTextBufferProvider openTextBufferProvider, IVsEditorAdaptersFactoryService editorAdaptersFactoryService, @@ -77,17 +86,19 @@ public SourceGeneratedFileManager( IAsynchronousOperationListenerProvider listenerProvider) { _serviceProvider = serviceProvider; + _vsInfoBarUIFactory = vsInfoBarUIFactory; + _vsShell = vsShell; _threadingContext = threadingContext; - _foregroundThreadAffinitizedObject = new ForegroundThreadAffinitizedObject(threadingContext, assertIsForeground: false); _textDocumentFactoryService = textDocumentFactoryService; _temporaryDirectory = PathUtilities.EnsureTrailingSeparator(Path.Combine(Path.GetTempPath(), "VSGeneratedDocuments")); _visualStudioWorkspace = visualStudioWorkspace; _visualStudioDocumentNavigationService = visualStudioDocumentNavigationService; - Directory.CreateDirectory(_temporaryDirectory); - + _listenerProvider = listenerProvider; _listener = listenerProvider.GetListener(FeatureAttribute.SourceGenerators); + Directory.CreateDirectory(_temporaryDirectory); + openTextBufferProvider.AddListener(this); } @@ -146,7 +157,7 @@ public bool TryGetGeneratedFileInformation( string filePath, out SourceGeneratedDocumentIdentity identity) { - _foregroundThreadAffinitizedObject.AssertIsForeground(); + _threadingContext.ThrowIfNotOnUIThread(); identity = default; @@ -176,7 +187,7 @@ public bool TryGetGeneratedFileInformation( void IOpenTextBufferEventListener.OnOpenDocument(string moniker, ITextBuffer textBuffer, IVsHierarchy? hierarchy) { - _foregroundThreadAffinitizedObject.AssertIsForeground(); + _threadingContext.ThrowIfNotOnUIThread(); if (TryGetGeneratedFileInformation(moniker, out var documentIdentity)) { @@ -199,14 +210,12 @@ void IOpenTextBufferEventListener.OnOpenDocument(string moniker, ITextBuffer tex void IOpenTextBufferEventListener.OnDocumentOpenedIntoWindowFrame(string moniker, IVsWindowFrame windowFrame) { if (_openFiles.TryGetValue(moniker, out var openFile)) - { - openFile.SetWindowFrame(windowFrame); - } + _threadingContext.JoinableTaskFactory.Run(() => openFile.SetWindowFrameAsync(windowFrame)); } void IOpenTextBufferEventListener.OnCloseDocument(string moniker) { - _foregroundThreadAffinitizedObject.AssertIsForeground(); + _threadingContext.ThrowIfNotOnUIThread(); if (_openFiles.TryGetValue(moniker, out var openFile)) { @@ -223,7 +232,7 @@ void IOpenTextBufferEventListener.OnRenameDocument(string newMoniker, string old { } - private class OpenSourceGeneratedFile : ForegroundThreadAffinitizedObject, IDisposable + private sealed class OpenSourceGeneratedFile : ForegroundThreadAffinitizedObject, IDisposable { private readonly SourceGeneratedFileManager _fileManager; private readonly ITextBuffer _textBuffer; @@ -250,16 +259,13 @@ private class OpenSourceGeneratedFile : ForegroundThreadAffinitizedObject, IDisp private readonly AsyncBatchingWorkQueue _batchingWorkQueue; /// - /// The of the active window. This may be null if we're in the middle of construction and - /// we haven't been given it yet. + /// The info bar of the active window. This may be null if we're in the middle of construction and we haven't + /// created it yet /// - private IVsWindowFrame? _windowFrame; + private VisualStudioInfoBar? _infoBar; + private VisualStudioInfoBar.InfoBarMessage? _currentInfoBarMessage; - private string? _windowFrameMessageToShow = null; - private ImageMoniker _windowFrameImageMonikerToShow = default; - private string? _currentWindowFrameMessage = null; - private ImageMoniker _currentWindowFrameImageMoniker = default; - private IVsInfoBarUIElement? _currentWindowFrameInfoBarElement = null; + private InfoBarInfo? _infoToShow = null; public OpenSourceGeneratedFile(SourceGeneratedFileManager fileManager, ITextBuffer textBuffer, Workspace workspace, SourceGeneratedDocumentIdentity documentIdentity, IThreadingContext threadingContext) : base(threadingContext, assertIsForeground: true) @@ -333,21 +339,21 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) // Locals correspond to the equivalently-named fields; we'll assign these and then assign to the fields while on the // UI thread to avoid any potential race where we update the InfoBar while this is running. - string? windowFrameMessageToShow; - ImageMoniker windowFrameImageMonikerToShow; + InfoBarInfo infoToShow; if (project == null) { - windowFrameMessageToShow = "The project no longer exists."; - windowFrameImageMonikerToShow = KnownMonikers.StatusError; + infoToShow = (KnownMonikers.StatusError, ServicesVSResources.The_project_no_longer_exists); } else { generatedDocument = await project.GetSourceGeneratedDocumentAsync(_documentIdentity.DocumentId, cancellationToken).ConfigureAwait(false); if (generatedDocument != null) { - windowFrameMessageToShow = string.Format(ServicesVSResources.This_file_is_autogenerated_by_0_and_cannot_be_edited, GeneratorDisplayName); - windowFrameImageMonikerToShow = default; + infoToShow = (imageMoniker: default, string.Format( + ServicesVSResources.This_file_was_generated_by_0_at_1_and_cannot_be_edited, + GeneratorDisplayName, + generatedDocument.GenerationDateTime.ToLocalTime())); generatedSource = await generatedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); } else @@ -355,21 +361,18 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) // The file isn't there anymore; do we still have the generator at all? if (project.AnalyzerReferences.Any(a => a.FullPath == _documentIdentity.Generator.AssemblyPath)) { - windowFrameMessageToShow = string.Format(ServicesVSResources.The_generator_0_that_generated_this_file_has_stopped_generating_this_file, GeneratorDisplayName); - windowFrameImageMonikerToShow = KnownMonikers.StatusError; + infoToShow = (KnownMonikers.StatusError, string.Format(ServicesVSResources.The_generator_0_that_generated_this_file_has_stopped_generating_this_file, GeneratorDisplayName)); } else { - windowFrameMessageToShow = string.Format(ServicesVSResources.The_generator_0_that_generated_this_file_has_been_removed_from_the_project, GeneratorDisplayName); - windowFrameImageMonikerToShow = KnownMonikers.StatusError; + infoToShow = (KnownMonikers.StatusError, string.Format(ServicesVSResources.The_generator_0_that_generated_this_file_has_been_removed_from_the_project, GeneratorDisplayName)); } } } await ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - _windowFrameMessageToShow = windowFrameMessageToShow; - _windowFrameImageMonikerToShow = windowFrameImageMonikerToShow; + _infoToShow = infoToShow; // Update the text if we have new text if (generatedSource != null) @@ -423,7 +426,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) } // Update the InfoBar either way - EnsureWindowFrameInfoBarUpdated(); + await EnsureWindowFrameInfoBarUpdatedAsync(cancellationToken).ConfigureAwait(true); } private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) @@ -435,75 +438,62 @@ private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) { // We'll start this work asynchronously to figure out if we need to change; if the file is closed the cancellationToken // is triggered and this will no-op. - var asyncToken = _fileManager._listener.BeginAsyncOperation(nameof(OpenSourceGeneratedFile) + "." + nameof(OnWorkspaceChanged)); + var asyncToken = _fileManager._listener.BeginAsyncOperation($"{nameof(OpenSourceGeneratedFile)}.{nameof(OnWorkspaceChanged)}"); + var cancellationToken = _cancellationTokenSource.Token; Task.Run(async () => { - if (await oldProject.GetDependentVersionAsync(_cancellationTokenSource.Token).ConfigureAwait(false) != - await newProject.GetDependentVersionAsync(_cancellationTokenSource.Token).ConfigureAwait(false)) + if (await oldProject.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false) != + await newProject.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false)) { _batchingWorkQueue.AddWork(); } - }, _cancellationTokenSource.Token).CompletesAsyncOperation(asyncToken); + }, cancellationToken).CompletesAsyncOperation(asyncToken); } } - internal void SetWindowFrame(IVsWindowFrame windowFrame) + internal async Task SetWindowFrameAsync(IVsWindowFrame windowFrame) { - AssertIsForeground(); + var cancellationToken = _cancellationTokenSource.Token; + await _fileManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - if (_windowFrame != null) - { - // We already have a window frame, and we don't expect to get a second one + // Only need to do this once. Can use the presence of the info bar to check this. + if (_infoBar != null) return; - } - _windowFrame = windowFrame; + _infoBar = new VisualStudioInfoBar( + this.ThreadingContext, _fileManager._vsInfoBarUIFactory, _fileManager._vsShell, _fileManager._listenerProvider, windowFrame); // We'll override the window frame and never show it as dirty, even if there's an underlying edit windowFrame.SetProperty((int)__VSFPROPID2.VSFPROPID_OverrideDirtyState, false); windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideCaption, _documentIdentity.HintName + " " + ServicesVSResources.generated_suffix); windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideToolTip, _documentIdentity.HintName + " " + string.Format(ServicesVSResources.generated_by_0_suffix, GeneratorDisplayName)); - EnsureWindowFrameInfoBarUpdated(); + await EnsureWindowFrameInfoBarUpdatedAsync(cancellationToken).ConfigureAwait(true); } - private void EnsureWindowFrameInfoBarUpdated() + private async Task EnsureWindowFrameInfoBarUpdatedAsync(CancellationToken cancellationToken) { - AssertIsForeground(); + await _fileManager._threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - if (_windowFrameMessageToShow == null || - _windowFrame == null || - _currentWindowFrameMessage == _windowFrameMessageToShow && - !_currentWindowFrameImageMoniker.Equals(_windowFrameImageMonikerToShow)) - { - // We don't have anything to do, or anything to do yet. + // If we don't have a frame, or even a message, we can't do anything yet. + if (_infoToShow is null || _infoBar is null) return; - } - var infoBarFactory = (IVsInfoBarUIFactory)_fileManager._serviceProvider.GetService(typeof(SVsInfoBarUIFactory)); - Assumes.Present(infoBarFactory); - - if (ErrorHandler.Failed(_windowFrame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out var infoBarHostObject)) || - infoBarHostObject is not IVsInfoBarHost infoBarHost) + // bail out if no change is needed + var (imageMoniker, message) = _infoToShow.Value; + if (_currentInfoBarMessage != null && + _currentInfoBarMessage.Message == message && + _currentInfoBarMessage.ImageMoniker.Equals(imageMoniker)) { return; } - // Remove the existing bar - if (_currentWindowFrameInfoBarElement != null) - { - infoBarHost.RemoveInfoBar(_currentWindowFrameInfoBarElement); - } - - var infoBar = new InfoBarModel(_windowFrameMessageToShow, _windowFrameImageMonikerToShow, isCloseButtonVisible: false); - var infoBarUI = infoBarFactory.CreateInfoBar(infoBar); - - infoBarHost.AddInfoBar(infoBarUI); + // Remove the current message so it can be replaced with the new one. + _currentInfoBarMessage?.Remove(); - _currentWindowFrameMessage = _windowFrameMessageToShow; - _currentWindowFrameImageMoniker = _windowFrameImageMonikerToShow; - _currentWindowFrameInfoBarElement = infoBarUI; + _currentInfoBarMessage = await _infoBar.ShowInfoBarMessageAsync( + message, isCloseButtonVisible: false, imageMoniker).ConfigureAwait(true); } public Task NavigateToSpanAsync(TextSpan sourceSpan, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioWorkspaceStatusServiceFactory.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioWorkspaceStatusServiceFactory.cs index 4909619e4e1e3..5d4eed5781bde 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioWorkspaceStatusServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioWorkspaceStatusServiceFactory.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.OperationProgress; @@ -23,55 +22,29 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation; [ExportWorkspaceServiceFactory(typeof(IWorkspaceStatusService), ServiceLayer.Host), Shared] -internal sealed class VisualStudioWorkspaceStatusServiceFactory : IWorkspaceServiceFactory +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class VisualStudioWorkspaceStatusServiceFactory( + SVsServiceProvider serviceProvider, + IThreadingContext threadingContext, + IAsynchronousOperationListenerProvider listenerProvider) : IWorkspaceServiceFactory { - private static readonly Option2 s_partialLoadModeFeatureFlag = new("visual_studio_workspace_partial_load_mode", defaultValue: false); - - private readonly IAsyncServiceProvider2 _serviceProvider; - private readonly IThreadingContext _threadingContext; - private readonly IGlobalOptionService _globalOptions; - private readonly IAsynchronousOperationListener _listener; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VisualStudioWorkspaceStatusServiceFactory( - SVsServiceProvider serviceProvider, - IThreadingContext threadingContext, - IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider) - { - _serviceProvider = (IAsyncServiceProvider2)serviceProvider; - _threadingContext = threadingContext; - _globalOptions = globalOptions; - - // for now, we use workspace so existing tests can automatically wait for full solution load event - // subscription done in test - _listener = listenerProvider.GetListener(FeatureAttribute.Workspace); - } + private readonly IAsyncServiceProvider2 _serviceProvider = (IAsyncServiceProvider2)serviceProvider; + private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.Workspace); [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) { - if (workspaceServices.Workspace is VisualStudioWorkspace) - { - if (!_globalOptions.GetOption(s_partialLoadModeFeatureFlag)) - { - // don't enable partial load mode for ones that are not in experiment yet - return new WorkspaceStatusService(); - } - - // only VSWorkspace supports partial load mode - return new Service(_serviceProvider, _threadingContext, _listener); - } - - return new WorkspaceStatusService(); + return workspaceServices.Workspace is VisualStudioWorkspace + ? new Service(_serviceProvider, threadingContext, _listener) + : new DefaultWorkspaceStatusService(); } /// /// for prototype, we won't care about what solution is actually fully loaded. /// we will just see whatever solution VS has at this point of time has actually fully loaded /// - private class Service : IWorkspaceStatusService + private sealed class Service : IWorkspaceStatusService { private readonly IAsyncServiceProvider2 _serviceProvider; private readonly IThreadingContext _threadingContext; diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index d1e7bfbf1c0bb..43b5f4ffe8724 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Spustit analýzu kódu v samostatném procesu (vyžaduje restartování) - - Run code analysis on latest .NET (requires restart) - Spustit analýzu kódu na nejnovějším rozhraní .NET (vyžaduje restartování) - - Running code analysis for '{0}'... Spouští se analýza kódu pro {0}... @@ -1252,6 +1247,16 @@ Nastavení hledání + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Výběrem vhodného symbolu zahájíte sledování hodnot. @@ -1292,6 +1297,16 @@ Vybrat členy: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Zobrazit příkaz Odebrat nepoužívané odkazy v Průzkumníkovi řešení (experimentální) @@ -1317,6 +1332,11 @@ Zobrazit seznam pro doplňování + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Zobrazit nápovědy pro všechno ostatní @@ -1377,6 +1397,11 @@ Trasování zásobníku: {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Návrh @@ -1437,14 +1462,19 @@ Generátor {0}, který vygeneroval tento soubor, přestal tento soubor generovat. Soubor už není součástí projektu. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Tato akce se nedá vrátit. Chcete pokračovat? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Tento soubor se automaticky vygeneroval pomocí generátoru {0} a nedá se upravit. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 545fe7c9d2db4..553beed67788d 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Codeanalyse in separatem Prozess ausführen (Neustart erforderlich) - - Run code analysis on latest .NET (requires restart) - Codeanalyse für die neueste .NET-Version ausführen (Neustart erforderlich) - - Running code analysis for '{0}'... Die Codeanalyse für "{0}" wird ausgeführt... @@ -1252,6 +1247,16 @@ Sucheinstellungen + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Wählen Sie ein geeignetes Symbol aus, um die Wertnachverfolgung zu starten @@ -1292,6 +1297,16 @@ Member auswählen: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Befehl "Nicht verwendete Verweise entfernen" in Projektmappen-Explorer anzeigen (experimentell) @@ -1317,6 +1332,11 @@ Vervollständigungsliste anzeigen + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Hinweise für alles andere anzeigen @@ -1377,6 +1397,11 @@ Stapelüberwachung {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Vorschlag @@ -1437,14 +1462,19 @@ Der Generator "{0}" hat diese Datei nicht vollständig generiert. Diese Datei wird nicht mehr in Ihr Projekt einbezogen. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie den Vorgang fortsetzen? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Diese Datei wird automatisch vom Generator "{0}" generiert und kann nicht bearbeitet werden. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index a096c54a69956..e7a2d56cfc294 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Ejecutar el análisis de código en un proceso independiente (requiere reiniciar) - - Run code analysis on latest .NET (requires restart) - Ejecutar análisis de código en la versión más reciente de .NET (requiere reiniciar) - - Running code analysis for '{0}'... Ejecutando el análisis de código para "{0}"... @@ -1252,6 +1247,16 @@ Buscar configuración + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Seleccione un símbolo adecuado para iniciar el seguimiento de valores @@ -1292,6 +1297,16 @@ Seleccionar miembros: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Mostrar el comando "Quitar referencias sin usar" en el Explorador de soluciones (experimental) @@ -1317,6 +1332,11 @@ Mostrar lista de finalización + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Mostrar sugerencias para todo lo demás @@ -1377,6 +1397,11 @@ Seguimiento de la pila {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Sugerencia @@ -1437,14 +1462,19 @@ El generador "{0}" que creaba este archivo ha dejado de generarlo; el archivo ya no se incluye en el proyecto. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Esta acción no se puede deshacer. ¿Quiere continuar? - - This file is auto-generated by the generator '{0}' and cannot be edited. - El generador "{0}" crea este archivo de forma automática y no se puede editar. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 6265ab847ad9d..b6896b74cca76 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Exécuter l’analyse du code dans un processus séparé (requiert un redémarrage) - - Run code analysis on latest .NET (requires restart) - Exécutez l'analyse de code sur le dernier .NET (nécessite un redémarrage) - - Running code analysis for '{0}'... Exécution de l'analyse du code pour '{0}'... @@ -1252,6 +1247,16 @@ Paramètres de recherche + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Sélectionnez un symbole approprié pour démarrer le suivi des valeurs @@ -1292,6 +1297,16 @@ Sélectionner les membres : + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Afficher la commande Supprimer les références inutilisées dans l'Explorateur de solutions (expérimental) @@ -1317,6 +1332,11 @@ Afficher la liste de saisie semi-automatique + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Afficher les indicateurs pour tout le reste @@ -1377,6 +1397,11 @@ Trace de la pile {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Suggestion @@ -1437,14 +1462,19 @@ Le générateur '{0}' qui a généré ce fichier a arrêté de générer ce dernier. Le fichier n'est plus inclus dans votre projet. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Il est impossible d'annuler cette action. Voulez-vous continuer ? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Ce fichier est généré automatiquement par le générateur '{0}' et ne peut pas être modifié. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index bac04d1a313bf..85da1bea60545 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Esegui analisi codice in un processo separato (richiede il riavvio) - - Run code analysis on latest .NET (requires restart) - Esegui Code Analysis nella versione più recente di .NET (richiede il riavvio) - - Running code analysis for '{0}'... Esecuzione di Code Analysis per '{0}'... @@ -1252,6 +1247,16 @@ Impostazioni di ricerca + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Selezionare un simbolo appropriato per avviare il rilevamento dei valori @@ -1292,6 +1297,16 @@ Selezionare i membri: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Mostra il comando "Rimuovi riferimenti inutilizzati" in Esplora soluzioni (sperimentale) @@ -1317,6 +1332,11 @@ Mostra l'elenco di completamento + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Mostra suggerimenti per tutto il resto @@ -1377,6 +1397,11 @@ Analisi dello stack {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Suggerimento @@ -1437,14 +1462,19 @@ Il generatore '{0}' che ha generato questo file non genera più il file. Questo file non è più incluso nel progetto. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Questa azione non può essere annullata. Continuare? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Questo file è stato generato automaticamente dal generatore '{0}' e non è modificabile. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 6627fa184ad96..7756e67f39d20 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ 別のプロセスでコード分析を実行する (再起動が必要) - - Run code analysis on latest .NET (requires restart) - 最新の .NET でコード分析を実行する (再起動が必要) - - Running code analysis for '{0}'... '{0}' のコード分析を実行しています... @@ -1252,6 +1247,16 @@ 検索設定 + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking 値の追跡を開始するための適切なシンボルを選択します @@ -1292,6 +1297,16 @@ メンバーの選択: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) ソリューション エクスプローラーで [未使用の参照を削除する] コマンドを表示する (試験段階) @@ -1317,6 +1332,11 @@ 入力候補一覧の表示 + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else その他すべてのヒントを表示する @@ -1377,6 +1397,11 @@ スタック トレース {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion 提案事項 @@ -1437,14 +1462,19 @@ このファイルの生成元であるジェネレーター '{0}' で、このファイルの生成が停止しました。このファイルはもうプロジェクトに含まれていません。 + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? この操作を元に戻すことはできません。続行しますか? - - This file is auto-generated by the generator '{0}' and cannot be edited. - このファイルはジェネレーター '{0}' によって自動生成されているため、編集できません。 + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 19f7d6be1920a..b4a8777b230d2 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0}({1}) @@ -1227,11 +1227,6 @@ 별도의 프로세스에서 코드 분석 실행(다시 시작해야 함) - - Run code analysis on latest .NET (requires restart) - 최신 .NET에서 코드 분석 실행(다시 시작 필요) - - Running code analysis for '{0}'... '{0}'에 대한 코드 분석을 실행하는 중... @@ -1252,6 +1247,16 @@ 검색 설정 + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking 적절한 기호를 선택하여 값 추적 시작 @@ -1292,6 +1297,16 @@ 멤버 선택: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) 솔루션 탐색기에서 "사용하지 않는 참조 제거" 명령 표시(실험적) @@ -1317,6 +1332,11 @@ 완성 목록 표시 + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else 다른 모든 항목에 대한 힌트 표시 @@ -1377,6 +1397,11 @@ 스택 추적 {0}개 Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion 제안 @@ -1437,14 +1462,19 @@ 이 파일을 생성한 생성기 '{0}'이(가) 이 파일 생성을 중지했습니다. 이 파일은 프로젝트에 더 이상 포함되지 않습니다. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? 이 작업은 실행 취소할 수 없습니다. 계속하시겠습니까? - - This file is auto-generated by the generator '{0}' and cannot be edited. - 이 파일은 생성기 '{0}'(으)로 자동 생성되며 편집할 수 없습니다. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 7f6c51ab3a166..b853e56e918c1 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Uruchom analizę kodu w oddzielnym procesie (wymaga ponownego uruchomienia) - - Run code analysis on latest .NET (requires restart) - Uruchamianie analizy kodu na najnowszej platformie .NET (wymaga ponownego uruchomienia) - - Running code analysis for '{0}'... Trwa analizowanie kodu dla elementu „{0}”... @@ -1252,6 +1247,16 @@ Ustawienia wyszukiwania + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Wybierz odpowiedni symbol, aby rozpocząć śledzenie wartości @@ -1292,6 +1297,16 @@ Wybierz składowe: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Pokaż polecenie „Usuń nieużywane odwołania” w Eksploratorze rozwiązań (eksperymentalne) @@ -1317,6 +1332,11 @@ Pokaż listę uzupełniania + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Pokaż wskazówki dla wszystkich innych elementów @@ -1377,6 +1397,11 @@ Ślad stosu {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Sugestia @@ -1437,14 +1462,19 @@ Generator „{0}”, który wygenerował ten plik, przestał generować ten plik; ten plik nie jest już uwzględniany w Twoim projekcie. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Tej akcji nie można cofnąć. Czy chcesz kontynuować? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Ten plik jest generowany automatycznie przez generator „{0}” i nie może być edytowany. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 4ae3db17961c4..0e5fc28054015 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Execute a análise de código em um processo separado (requer reinicialização) - - Run code analysis on latest .NET (requires restart) - Executar análise de código no .NET mais recente (requer reinicialização) - - Running code analysis for '{0}'... Executando a análise de código para '{0}'... @@ -1252,6 +1247,16 @@ Pesquisar as Configurações + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Selecionar um símbolo apropriado para iniciar o rastreamento de valor @@ -1292,6 +1297,16 @@ Selecionar membros: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Mostrar o comando "Remover as Referências Não Usadas" no Gerenciador de Soluções (experimental) @@ -1317,6 +1332,11 @@ Mostrar a lista de conclusão + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Mostrar as dicas para tudo @@ -1377,6 +1397,11 @@ Rastreamento de Pilha{0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Sugestão @@ -1437,14 +1462,19 @@ O gerador '{0}' que gerou esse arquivo parou de gerá-lo. Esse arquivo não será mais incluído no projeto. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Esta ação não pode ser desfeita. Deseja continuar? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Esse arquivo é gerado automaticamente pelo gerador '{0}' e não pode ser editado. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 7f92908b97a19..011f768e56f5c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Запуск анализа кода в отдельном процессе (требуется перезапуск) - - Run code analysis on latest .NET (requires restart) - Выполнить анализ кода на последней версии .NET (требуется перезапуск) - - Running code analysis for '{0}'... Выполняется анализ кода для "{0}"… @@ -1252,6 +1247,16 @@ Параметры поиска + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Выберите соответствующий символ, чтобы начать отслеживание значений @@ -1292,6 +1297,16 @@ Выбрать элементы: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Показать команду "Удалить неиспользуемые ссылки" в Обозревателе решений (экспериментальная версия) @@ -1317,6 +1332,11 @@ Показать список завершения + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Отображать подсказки для всех остальных элементов @@ -1377,6 +1397,11 @@ Трассировка стека {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Рекомендация @@ -1437,14 +1462,19 @@ Генератор "{0}", создавший этот файл, остановил создание этого файла; этот файл больше не включен в проект. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Это действие не может быть отменено. Вы хотите продолжить? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Этот файл создан автоматически генератором "{0}" и не может быть изменен. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 88b236ef47313..2596b2cc86d3e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ Kod analizini ayrı bir işlemde çalıştır (yeniden başlatma gerektirir) - - Run code analysis on latest .NET (requires restart) - En son .NET'te kod analizi çalıştırın (yeniden başlatma gerektirir) - - Running code analysis for '{0}'... '{0}' için kod analizi çalıştırılıyor... @@ -1252,6 +1247,16 @@ Arama Ayarları + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking Değer takibini başlatmak için uygun bir sembol seçin @@ -1292,6 +1297,16 @@ Üye seçin: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) Çözüm Gezgini'nde "Kullanılmayan Başvuruları Kaldır" komutunu göster (deneysel) @@ -1317,6 +1332,11 @@ Tamamlama listesini göster + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else Diğer her şey için ipuçlarını göster @@ -1377,6 +1397,11 @@ Yığın İzleme {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion Öneri @@ -1437,14 +1462,19 @@ Bu dosyayı oluşturan '{0}' oluşturucusu bu dosyayı oluşturmayı durdurdu; bu dosya artık projenize dahil edilmiyor. + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? Bu işlem geri alınamaz. Devam etmek istiyor musunuz? - - This file is auto-generated by the generator '{0}' and cannot be edited. - Bu dosya, '{0}' oluşturucusu tarafından otomatik olarak oluşturuldu ve düzenlenemez. + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index b84781af1c18d..70a2e35c73e17 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ 在单独的进程中运行代码分析(需要重启) - - Run code analysis on latest .NET (requires restart) - 在最新 .NET 上运行代码分析(需要重启) - - Running code analysis for '{0}'... 正在为“{0}”运行代码分析… @@ -1252,6 +1247,16 @@ 搜索设置 + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking 选择适当的符号以启动值跟踪 @@ -1292,6 +1297,16 @@ 选择成员: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) 在解决方案资源管理器中显示“删除未使用的引用”命令(实验性) @@ -1317,6 +1332,11 @@ 显示完成列表 + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else 显示其他所有内容的提示 @@ -1377,6 +1397,11 @@ 堆栈跟踪: {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion 建议 @@ -1437,14 +1462,19 @@ 生成此文件的生成器“{0}”已停止生成此文件;项目中不再包含此文件。 + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? 此操作无法撤消。是否要继续? - - This file is auto-generated by the generator '{0}' and cannot be edited. - 此文件由生成器“{0}”自动生成,无法编辑。 + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index 6acce6f8d8af4..6f426f60fb8b8 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -119,7 +119,7 @@ {0} ({1}) - {0} ({1}) + {0} ({1}) @@ -1227,11 +1227,6 @@ 在獨立的流程中執行程式碼分析 (需要重新開機) - - Run code analysis on latest .NET (requires restart) - 針對最新的 .NET 執行程式碼分析 (需要重新啟動) - - Running code analysis for '{0}'... 正在執行 '{0}' 的程式碼分析... @@ -1252,6 +1247,16 @@ 搜尋設定 + + Search canceled + Search canceled + + + + Search found no results: no C# or Visual Basic projects are opened + Search found no results: no C# or Visual Basic projects are opened + + Select an appropriate symbol to start value tracking 選取適當的符號以開始值追蹤 @@ -1292,6 +1297,16 @@ 選取成員: + + Semantic Search ({0}) + Semantic Search ({0}) + + + + Semantic Search Results + Semantic Search Results + + Show "Remove Unused References" command in Solution Explorer (experimental) 在方案總管 (實驗性) 中顯示「移除未使用的參考」命令 @@ -1317,6 +1332,11 @@ 顯示自動完成清單 + + Show guides for comments and preprocessor regions + Show guides for comments and preprocessor regions + + Show hints for everything else 顯示所有其他項目的提示 @@ -1377,6 +1397,11 @@ 堆疊追蹤 {0} Header for numbered stack trace view tabs + + Strike out obsolete symbols + Strike out obsolete symbols + + Suggestion 建議 @@ -1437,14 +1462,19 @@ 產生此檔案的產生器 '{0}' 已停止產生此檔案; 此檔案已不再包含在您的專案中。 + + The project no longer exists + The project no longer exists + + This action cannot be undone. Do you wish to continue? 此動作無法復原。要繼續嗎? - - This file is auto-generated by the generator '{0}' and cannot be edited. - 此檔案是由產生器 '{0}' 自動產生,無法加以編輯。 + + This file was generated by '{0}' at {1} and cannot be edited. + This file was generated by '{0}' at {1} and cannot be edited. diff --git a/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs b/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs index 3ad47a5d3fa47..45aeb866aeb75 100644 --- a/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs +++ b/src/VisualStudio/Core/Impl/Options/AbstractOptionPageControl.cs @@ -191,6 +191,40 @@ private protected void BindToOption(ComboBox comboBox, PerLanguageOption2 _bindingExpressions.Add(bindingExpression); } + private protected void BindToOption(RadioButton radiobutton, Option2 optionKey, T optionValue) + { + var binding = new Binding() + { + Source = new OptionBinding(OptionStore, optionKey), + Path = new PropertyPath("Value"), + UpdateSourceTrigger = UpdateSourceTrigger.Default, + Converter = new RadioButtonCheckedConverter(), + ConverterParameter = optionValue + }; + + AddSearchHandler(radiobutton); + + var bindingExpression = radiobutton.SetBinding(RadioButton.IsCheckedProperty, binding); + _bindingExpressions.Add(bindingExpression); + } + + private protected void BindToOption(RadioButton radioButton, Option2 nullableOptionKey, T optionValue, Func onNullValue) where T : struct + { + var binding = new Binding() + { + Source = new OptionBinding(OptionStore, nullableOptionKey), + Path = new PropertyPath("Value"), + UpdateSourceTrigger = UpdateSourceTrigger.Default, + Converter = new RadioButtonCheckedConverter(onNullValue), + ConverterParameter = optionValue, + }; + + AddSearchHandler(radioButton); + + var bindingExpression = radioButton.SetBinding(RadioButton.IsCheckedProperty, binding); + _bindingExpressions.Add(bindingExpression); + } + private protected void BindToOption(RadioButton radiobutton, PerLanguageOption2 optionKey, T optionValue, string languageName) { var binding = new Binding() @@ -255,19 +289,26 @@ private protected void AddSearchHandler(ContentControl control) } } - public class RadioButtonCheckedConverter : IValueConverter + public sealed class RadioButtonCheckedConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, - System.Globalization.CultureInfo culture) - { - return value.Equals(parameter); - } + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + => value.Equals(parameter); - public object ConvertBack(object value, Type targetType, object parameter, - System.Globalization.CultureInfo culture) - { - return value.Equals(true) ? parameter : Binding.DoNothing; - } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => value.Equals(true) ? parameter : Binding.DoNothing; + } + + public sealed class RadioButtonCheckedConverter(Func onNullValue) : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + => value switch + { + null => onNullValue(), + _ => value.Equals(parameter), + }; + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => value.Equals(true) ? parameter : Binding.DoNothing; } public class ComboBoxItemTagToIndexConverter : IValueConverter diff --git a/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs b/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs index ffe8fd7fd083a..1d485871823e2 100644 --- a/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs +++ b/src/VisualStudio/Core/Impl/Options/Converters/NullableBoolOptionConverter.cs @@ -7,25 +7,18 @@ using System.Windows; using System.Windows.Data; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Converters +namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options.Converters; + +internal sealed class NullableBoolOptionConverter(Func onNullValue) : IValueConverter { - internal class NullableBoolOptionConverter : IValueConverter - { - private readonly Func _onNullValue; - public NullableBoolOptionConverter(Func onNullValue) + public object Convert(object? value, Type targetType, object parameter, CultureInfo culture) + => value switch { - _onNullValue = onNullValue; - } - - public object Convert(object? value, Type targetType, object parameter, CultureInfo culture) - => value switch - { - null => _onNullValue(), - bool b => b, - _ => DependencyProperty.UnsetValue - }; + null => onNullValue(), + bool b => b, + _ => DependencyProperty.UnsetValue + }; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - => value; - } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + => value; } diff --git a/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs b/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs index 59fa7df3cca64..7381a03351a4f 100644 --- a/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs +++ b/src/VisualStudio/Core/Test.Next/Options/VisualStudioSettingsOptionPersisterTests.cs @@ -100,17 +100,6 @@ private static (object? value, object? storageValue) GetSomeOptionValue(Type opt optionType == typeof(ImmutableArray) ? (ImmutableArray.Create("a", "b"), new[] { "a", "b" }) : throw ExceptionUtilities.UnexpectedValue(optionType); - private static Type GetStorageType(Type optionType) - => optionType.IsEnum ? typeof(int) : - (Nullable.GetUnderlyingType(optionType)?.IsEnum == true) ? typeof(int?) : - optionType == typeof(NamingStylePreferences) ? typeof(string) : - typeof(ICodeStyleOption2).IsAssignableFrom(optionType) ? typeof(string) : - optionType == typeof(ImmutableArray) ? typeof(string[]) : - optionType == typeof(ImmutableArray) ? typeof(bool[]) : - optionType == typeof(ImmutableArray) ? typeof(int[]) : - optionType == typeof(ImmutableArray) ? typeof(long[]) : - optionType; - private static bool IsDefaultImmutableArray(object array) => (bool)array.GetType().GetMethod("get_IsDefault").Invoke(array, [])!; @@ -229,11 +218,10 @@ public void SettingsManagerReadOptionValue_Error( Type optionType) { var (optionValue, storageValue) = GetSomeOptionValue(optionType); - var storageType = GetStorageType(optionType); var mockManager = new MockSettingsManager() { - GetValueImpl = (_, type) => (type == storageType ? specializedTypeResult : GetValueResult.Success, storageValue) + GetValueImpl = (_, type) => (specializedTypeResult, storageValue) }; var result = VisualStudioSettingsOptionPersister.TryReadOptionValue(mockManager, "key", optionType, optionValue); diff --git a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs index 44e0d9b7ccaaa..bbb5f577a5433 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs @@ -535,7 +535,8 @@ public async Task EmptyAssetChecksumTest() var document = CreateWorkspace().CurrentSolution.AddProject("empty", "empty", LanguageNames.CSharp).AddDocument("empty", SourceText.From("")); var serializer = document.Project.Solution.Services.GetService(); - var source = serializer.CreateChecksum(await document.GetTextAsync().ConfigureAwait(false), CancellationToken.None); + var text = await document.GetTextAsync().ConfigureAwait(false); + var source = serializer.CreateChecksum(new SerializableSourceText(text, text.GetContentHash()), CancellationToken.None); var metadata = serializer.CreateChecksum(new MissingMetadataReference(), CancellationToken.None); var analyzer = serializer.CreateChecksum(new AnalyzerFileReference(Path.Combine(TempRoot.Root, "missing"), new MissingAnalyzerLoader()), CancellationToken.None); @@ -607,40 +608,42 @@ public void TestEncodingSerialization() // test with right serializable encoding var sourceText = SourceText.From("Hello", Encoding.UTF8); + var serializableSourceText = new SerializableSourceText(sourceText, sourceText.GetContentHash()); using (var stream = SerializableBytes.CreateWritableStream()) { using var context = new SolutionReplicationContext(); using (var objectWriter = new ObjectWriter(stream, leaveOpen: true)) { - serializer.Serialize(sourceText, objectWriter, context, CancellationToken.None); + serializer.Serialize(serializableSourceText, objectWriter, context, CancellationToken.None); } stream.Position = 0; using var objectReader = ObjectReader.TryGetReader(stream); - var newText = (SourceText)serializer.Deserialize(sourceText.GetWellKnownSynchronizationKind(), objectReader, CancellationToken.None); - Assert.Equal(sourceText.ToString(), newText.ToString()); + var newText = (SerializableSourceText)serializer.Deserialize(serializableSourceText.GetWellKnownSynchronizationKind(), objectReader, CancellationToken.None); + Assert.Equal(sourceText.ToString(), newText.GetText(CancellationToken.None).ToString()); } // test with wrong encoding that doesn't support serialization sourceText = SourceText.From("Hello", new NotSerializableEncoding()); + serializableSourceText = new SerializableSourceText(sourceText, sourceText.GetContentHash()); using (var stream = SerializableBytes.CreateWritableStream()) { using var context = new SolutionReplicationContext(); using (var objectWriter = new ObjectWriter(stream, leaveOpen: true)) { - serializer.Serialize(sourceText, objectWriter, context, CancellationToken.None); + serializer.Serialize(serializableSourceText, objectWriter, context, CancellationToken.None); } stream.Position = 0; using var objectReader = ObjectReader.TryGetReader(stream); - var newText = (SourceText)serializer.Deserialize(sourceText.GetWellKnownSynchronizationKind(), objectReader, CancellationToken.None); - Assert.Equal(sourceText.ToString(), newText.ToString()); + var newText = (SerializableSourceText)serializer.Deserialize(serializableSourceText.GetWellKnownSynchronizationKind(), objectReader, CancellationToken.None); + Assert.Equal(sourceText.ToString(), newText.GetText(CancellationToken.None).ToString()); } } diff --git a/src/VisualStudio/Core/Test.Next/Remote/SolutionAsset.cs b/src/VisualStudio/Core/Test.Next/Remote/SolutionAsset.cs index 379411f01b4d2..fcb3ed325c622 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SolutionAsset.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SolutionAsset.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Serialization; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; @@ -30,11 +29,6 @@ internal readonly struct SolutionAsset public SolutionAsset(Checksum checksum, object value) { var kind = value.GetWellKnownSynchronizationKind(); - // SolutionAsset is not allowed to hold strong references to SourceText. SerializableSourceText is used - // instead to allow data to be released from process address space when it is also held in temporary - // storage. - // https://github.com/dotnet/roslyn/issues/43802 - Contract.ThrowIfTrue(kind is WellKnownSynchronizationKind.SourceText); Checksum = checksum; Kind = kind; diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 30022d8d436a0..9ba50f5f37516 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -137,7 +137,7 @@ public async Task TestDesignerAttributes() // Ensure remote workspace is in sync with normal workspace. var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, solution.WorkspaceVersion, CancellationToken.None); + await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, CancellationToken.None); var callback = new DesignerAttributeComputerCallback(); @@ -188,7 +188,7 @@ public async Task TestDesignerAttributesUnsupportedLanguage() // Ensure remote workspace is in sync with normal workspace. var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, solution.WorkspaceVersion, CancellationToken.None); + await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, CancellationToken.None); var callback = new DesignerAttributeComputerCallback(); @@ -359,9 +359,8 @@ public async Task TestRemoteWorkspaceCircularReferences() using var remoteWorkspace = new RemoteWorkspace(FeaturesTestCompositions.RemoteHost.GetHostServices()); // this shouldn't throw exception - var (solution, updated) = await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync( - remoteWorkspace.GetTestAccessor().CreateSolutionFromInfo(solutionInfo), workspaceVersion: 1); - Assert.True(updated); + var solution = await remoteWorkspace.GetTestAccessor().UpdateWorkspaceCurrentSolutionAsync( + remoteWorkspace.GetTestAccessor().CreateSolutionFromInfo(solutionInfo)); Assert.NotNull(solution); } @@ -828,10 +827,9 @@ private static (Project project, ImmutableArray documents) GetProjectA private static async Task UpdatePrimaryWorkspace(RemoteHostClient client, Solution solution) { - var workspaceVersion = solution.WorkspaceVersion; await client.TryInvokeAsync( solution, - async (service, solutionInfo, cancellationToken) => await service.SynchronizePrimaryWorkspaceAsync(solutionInfo, workspaceVersion, cancellationToken), + async (service, solutionInfo, cancellationToken) => await service.SynchronizePrimaryWorkspaceAsync(solutionInfo, cancellationToken), CancellationToken.None); } diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 3f6703acfb73d..287ddfe63a30b 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -48,7 +48,7 @@ public async Task TestCreation() var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); } @@ -66,7 +66,7 @@ public async Task TestGetSolutionWithPrimaryFlag(bool updatePrimaryBranch) var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); - var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch, solution.WorkspaceVersion, cancellationToken: CancellationToken.None); + var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch, cancellationToken: CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); Assert.Equal(WorkspaceKind.RemoteWorkspace, synched.WorkspaceKind); @@ -88,7 +88,7 @@ public async Task TestStrongNameProvider() var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, workspace.CurrentSolution); var solutionChecksum = await workspace.CurrentSolution.CompilationState.GetChecksumAsync(CancellationToken.None); - var solution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var solution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); var compilationOptions = solution.Projects.First().CompilationOptions; @@ -117,7 +117,7 @@ public async Task TestStrongNameProviderEmpty() var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, workspace.CurrentSolution); var solutionChecksum = await workspace.CurrentSolution.CompilationState.GetChecksumAsync(CancellationToken.None); - var solution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var solution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); var compilationOptions = solution.Projects.First().CompilationOptions; @@ -141,8 +141,8 @@ public async Task TestCache() var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var first = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); - var second = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var first = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); + var second = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); // same instance from cache Assert.True(object.ReferenceEquals(first, second)); @@ -344,54 +344,49 @@ public async Task TestRemoteWorkspace() var remoteSolution1 = await GetInitialOOPSolutionAsync(remoteWorkspace, assetProvider, solution1); - await Verify(remoteWorkspace, solution1, remoteSolution1, expectRemoteSolutionToCurrent: true); - var version = solution1.WorkspaceVersion; + await Verify(remoteWorkspace, solution1, remoteSolution1); // update remote workspace var currentSolution = remoteSolution1.WithDocumentText(remoteSolution1.Projects.First().Documents.First().Id, SourceText.From(code + " class Test2 { }")); - var (oopSolution2, _) = await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync(currentSolution, ++version); + var oopSolution2 = await remoteWorkspace.GetTestAccessor().UpdateWorkspaceCurrentSolutionAsync(currentSolution); - await Verify(remoteWorkspace, currentSolution, oopSolution2, expectRemoteSolutionToCurrent: true); + await Verify(remoteWorkspace, currentSolution, oopSolution2); // move backward - await Verify(remoteWorkspace, remoteSolution1, (await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync(remoteSolution1, solution1.WorkspaceVersion)).solution, expectRemoteSolutionToCurrent: false); + await Verify(remoteWorkspace, remoteSolution1, await remoteWorkspace.GetTestAccessor().UpdateWorkspaceCurrentSolutionAsync(remoteSolution1)); // move forward currentSolution = oopSolution2.WithDocumentText(oopSolution2.Projects.First().Documents.First().Id, SourceText.From(code + " class Test3 { }")); - var remoteSolution3 = (await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync(currentSolution, ++version)).solution; + var remoteSolution3 = await remoteWorkspace.GetTestAccessor().UpdateWorkspaceCurrentSolutionAsync(currentSolution); - await Verify(remoteWorkspace, currentSolution, remoteSolution3, expectRemoteSolutionToCurrent: true); + await Verify(remoteWorkspace, currentSolution, remoteSolution3); // move to new solution backward var solutionInfo2 = await assetProvider.CreateSolutionInfoAsync(await solution1.CompilationState.GetChecksumAsync(CancellationToken.None), CancellationToken.None); var solution2 = remoteWorkspace.GetTestAccessor().CreateSolutionFromInfo(solutionInfo2); - Assert.False((await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync( - solution2, solution1.WorkspaceVersion)).updated); // move to new solution forward - var (solution3, updated3) = await remoteWorkspace.GetTestAccessor().TryUpdateWorkspaceCurrentSolutionAsync( - solution2, ++version); + var solution3 = await remoteWorkspace.GetTestAccessor().UpdateWorkspaceCurrentSolutionAsync(solution2); Assert.NotNull(solution3); - Assert.True(updated3); - await Verify(remoteWorkspace, solution1, solution3, expectRemoteSolutionToCurrent: true); + await Verify(remoteWorkspace, solution1, solution3); static async Task GetInitialOOPSolutionAsync(RemoteWorkspace remoteWorkspace, AssetProvider assetProvider, Solution solution) { // set up initial solution var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, solution.WorkspaceVersion, CancellationToken.None); + await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, CancellationToken.None); // get solution in remote host - return await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + return await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); } - static async Task Verify(RemoteWorkspace remoteWorkspace, Solution givenSolution, Solution remoteSolution, bool expectRemoteSolutionToCurrent) + static async Task Verify(RemoteWorkspace remoteWorkspace, Solution givenSolution, Solution remoteSolution) { // verify we got solution expected Assert.Equal(await givenSolution.CompilationState.GetChecksumAsync(CancellationToken.None), await remoteSolution.CompilationState.GetChecksumAsync(CancellationToken.None)); // verify remote workspace got updated - Assert.True(expectRemoteSolutionToCurrent == (remoteSolution == remoteWorkspace.CurrentSolution)); + Assert.Equal(remoteSolution, remoteWorkspace.CurrentSolution); } } @@ -407,7 +402,7 @@ public async Task TestAddingProjectsWithExplicitOptions(bool useDefaultOptionVal solution = solution.RemoveProject(solution.ProjectIds.Single()); var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: 0, CancellationToken.None); + var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); // Add a C# project and a VB project, set some options, and check again @@ -429,7 +424,7 @@ public async Task TestAddingProjectsWithExplicitOptions(bool useDefaultOptionVal assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: 2, CancellationToken.None); + synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); } @@ -447,26 +442,26 @@ public async Task TestFrozenSourceGeneratedDocument() // First sync the solution over that has a generator var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: 0, CancellationToken.None); + var synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); // Now freeze with some content var documentIdentity = (await solution.Projects.Single().GetSourceGeneratedDocumentsAsync()).First().Identity; var frozenText1 = SourceText.From("// Hello, World!"); - var frozenSolution1 = solution.WithFrozenSourceGeneratedDocument(documentIdentity, frozenText1).Project.Solution; + var frozenSolution1 = solution.WithFrozenSourceGeneratedDocument(documentIdentity, DateTime.Now, frozenText1).Project.Solution; assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, frozenSolution1); solutionChecksum = await frozenSolution1.CompilationState.GetChecksumAsync(CancellationToken.None); - synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: 1, CancellationToken.None); + synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); // Try freezing with some different content from the original solution var frozenText2 = SourceText.From("// Hello, World! A second time!"); - var frozenSolution2 = solution.WithFrozenSourceGeneratedDocument(documentIdentity, frozenText2).Project.Solution; + var frozenSolution2 = solution.WithFrozenSourceGeneratedDocument(documentIdentity, DateTime.Now, frozenText2).Project.Solution; assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, frozenSolution2); solutionChecksum = await frozenSolution2.CompilationState.GetChecksumAsync(CancellationToken.None); - synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: 2, CancellationToken.None); + synched = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.CompilationState.GetChecksumAsync(CancellationToken.None)); } @@ -493,7 +488,7 @@ public async Task TestPartialProjectSync_GetSolutionFirst() await solution.AppendAssetMapAsync(map, CancellationToken.None); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var syncedFullSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: solution.WorkspaceVersion, CancellationToken.None); + var syncedFullSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await syncedFullSolution.CompilationState.GetChecksumAsync(CancellationToken.None)); Assert.Equal(2, syncedFullSolution.Projects.Count()); @@ -501,13 +496,13 @@ public async Task TestPartialProjectSync_GetSolutionFirst() // Syncing project1 should do nothing as syncing the solution already synced it over. var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project1SyncedSolution.Projects.Count()); // Syncing project2 should do nothing as syncing the solution already synced it over. var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project2SyncedSolution.Projects.Count()); } @@ -533,20 +528,20 @@ public async Task TestPartialProjectSync_GetSolutionLast() // Syncing project 1 should just since it over. await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project1SyncedSolution.Projects.Count()); Assert.Equal(project1.Name, project1SyncedSolution.Projects.Single().Name); // Syncing project 2 should end up with only p2 synced over. await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project2SyncedSolution.Projects.Count()); // then syncing the whole project should now copy both over. await solution.AppendAssetMapAsync(map, CancellationToken.None); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var syncedFullSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: solution.WorkspaceVersion, CancellationToken.None); + var syncedFullSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(solutionChecksum, await syncedFullSolution.CompilationState.GetChecksumAsync(CancellationToken.None)); Assert.Equal(2, syncedFullSolution.Projects.Count()); @@ -574,14 +569,14 @@ public async Task TestPartialProjectSync_GetDependentProjects1() await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project2SyncedSolution.Projects.Count()); Assert.Equal(project2.Name, project2SyncedSolution.Projects.Single().Name); // syncing project 3 should sync project 2 as well because of the p2p ref await solution.AppendAssetMapAsync(map, project3.Id, CancellationToken.None); var project3Checksum = await solution.CompilationState.GetChecksumAsync(project3.Id, CancellationToken.None); - var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project3SyncedSolution.Projects.Count()); } @@ -608,20 +603,20 @@ public async Task TestPartialProjectSync_GetDependentProjects2() // syncing P3 should since project P2 as well because of the p2p ref await solution.AppendAssetMapAsync(map, project3.Id, CancellationToken.None); var project3Checksum = await solution.CompilationState.GetChecksumAsync(project3.Id, CancellationToken.None); - var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project3SyncedSolution.Projects.Count()); // if we then sync just P2, we should still have only P2 in the synced cone await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project2SyncedSolution.Projects.Count()); AssertEx.Equal(project2.Name, project2SyncedSolution.Projects.Single().Name); // if we then sync just P1, we should only have it in its own cone. await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project1SyncedSolution.Projects.Count()); AssertEx.Equal(project1.Name, project1SyncedSolution.Projects.Single().Name); } @@ -650,19 +645,19 @@ public async Task TestPartialProjectSync_GetDependentProjects3() // syncing project3 should since project2 and project1 as well because of the p2p ref await solution.AppendAssetMapAsync(map, project3.Id, CancellationToken.None); var project3Checksum = await solution.CompilationState.GetChecksumAsync(project3.Id, CancellationToken.None); - var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(3, project3SyncedSolution.Projects.Count()); // syncing project2 should only have it and project 1. await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project2SyncedSolution.Projects.Count()); // syncing project1 should only be itself await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project1SyncedSolution.Projects.Count()); } @@ -690,19 +685,19 @@ public async Task TestPartialProjectSync_GetDependentProjects4() // syncing project3 should since project2 and project1 as well because of the p2p ref await solution.AppendAssetMapAsync(map, project3.Id, CancellationToken.None); var project3Checksum = await solution.CompilationState.GetChecksumAsync(project3.Id, CancellationToken.None); - var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project3SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project3Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(3, project3SyncedSolution.Projects.Count()); // Syncing project2 should only have a cone with itself. await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project2SyncedSolution.Projects.Count()); // Syncing project1 should only have a cone with itself. await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project1SyncedSolution.Projects.Count()); } @@ -728,14 +723,14 @@ public async Task TestPartialProjectSync_Options1() // Syncing over project1 should give us 1 set of options on the OOP side. await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project1SyncedSolution.Projects.Count()); Assert.Equal(project1.Name, project1SyncedSolution.Projects.Single().Name); // Syncing over project2 should also only be one set of options. await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(1, project2SyncedSolution.Projects.Count()); } @@ -761,7 +756,7 @@ public async Task TestPartialProjectSync_DoesNotSeeChangesOutsideOfCone() // Do the initial full sync await solution.AppendAssetMapAsync(map, CancellationToken.None); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var fullSyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: solution.WorkspaceVersion, CancellationToken.None); + var fullSyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(2, fullSyncedSolution.Projects.Count()); // Mutate both projects to each have a document in it. @@ -773,7 +768,7 @@ public async Task TestPartialProjectSync_DoesNotSeeChangesOutsideOfCone() { await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project1SyncedSolution.Projects.Count()); var csharpProject = project1SyncedSolution.Projects.Single(p => p.Language == LanguageNames.CSharp); var vbProject = project1SyncedSolution.Projects.Single(p => p.Language == LanguageNames.VisualBasic); @@ -785,7 +780,7 @@ public async Task TestPartialProjectSync_DoesNotSeeChangesOutsideOfCone() { await solution.AppendAssetMapAsync(map, project2.Id, CancellationToken.None); var project2Checksum = await solution.CompilationState.GetChecksumAsync(project2.Id, CancellationToken.None); - var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project2SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project2Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project2SyncedSolution.Projects.Count()); var csharpProject = project2SyncedSolution.Projects.Single(p => p.Language == LanguageNames.CSharp); var vbProject = project2SyncedSolution.Projects.Single(p => p.Language == LanguageNames.VisualBasic); @@ -816,7 +811,7 @@ public async Task TestPartialProjectSync_AddP2PRef() // Do the initial full sync await solution.AppendAssetMapAsync(map, CancellationToken.None); var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); - var fullSyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, workspaceVersion: solution.WorkspaceVersion, CancellationToken.None); + var fullSyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: true, CancellationToken.None); Assert.Equal(2, fullSyncedSolution.Projects.Count()); // Mutate both projects to have a document in it, and add a p2p ref from project1 to project2 @@ -829,7 +824,7 @@ public async Task TestPartialProjectSync_AddP2PRef() { await solution.AppendAssetMapAsync(map, project1.Id, CancellationToken.None); var project1Checksum = await solution.CompilationState.GetChecksumAsync(project1.Id, CancellationToken.None); - var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var project1SyncedSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, project1Checksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(2, project1SyncedSolution.Projects.Count()); var project1Synced = project1SyncedSolution.GetRequiredProject(project1.Id); var project2Synced = project1SyncedSolution.GetRequiredProject(project2.Id); @@ -885,8 +880,8 @@ private static async Task VerifySolutionUpdate( var solutionChecksum = await solution.CompilationState.GetChecksumAsync(CancellationToken.None); // update primary workspace - await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, solution.WorkspaceVersion, CancellationToken.None); - var recoveredSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, CancellationToken.None); + var recoveredSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, CancellationToken.None); oldSolutionValidator?.Invoke(recoveredSolution); Assert.Equal(WorkspaceKind.RemoteWorkspace, recoveredSolution.WorkspaceKind); @@ -898,13 +893,13 @@ private static async Task VerifySolutionUpdate( await newSolution.AppendAssetMapAsync(map, CancellationToken.None); // get solution without updating primary workspace - var recoveredNewSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, newSolutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + var recoveredNewSolution = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, newSolutionChecksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(newSolutionChecksum, await recoveredNewSolution.CompilationState.GetChecksumAsync(CancellationToken.None)); // do same once updating primary workspace - await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, newSolutionChecksum, solution.WorkspaceVersion + 1, CancellationToken.None); - var third = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, newSolutionChecksum, updatePrimaryBranch: false, workspaceVersion: -1, CancellationToken.None); + await remoteWorkspace.UpdatePrimaryBranchSolutionAsync(assetProvider, newSolutionChecksum, CancellationToken.None); + var third = await remoteWorkspace.GetTestAccessor().GetSolutionAsync(assetProvider, newSolutionChecksum, updatePrimaryBranch: false, CancellationToken.None); Assert.Equal(newSolutionChecksum, await third.CompilationState.GetChecksumAsync(CancellationToken.None)); diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index 98091183d44f1..51c11425410a8 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -314,17 +314,5 @@ public override void Initialize(AnalysisContext context) }); } } - - private class MyUpdateSource : AbstractHostDiagnosticUpdateSource - { - private readonly Workspace _workspace; - - public MyUpdateSource(Workspace workspace) - { - _workspace = workspace; - } - - public override Workspace Workspace => _workspace; - } } } diff --git a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb index 3c03a208a23a3..912f2e7ef59e9 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/ExternalDiagnosticUpdateSourceTests.vb @@ -25,9 +25,7 @@ Imports Roslyn.Utilities Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics <[UseExportProvider]> Public Class ExternalDiagnosticUpdateSourceTests - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Public Sub TestExternalDiagnostics_SupportGetDiagnostics() @@ -283,7 +281,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim service = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim registration = service.CreateIncrementalAnalyzer(workspace) @@ -336,7 +333,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim service = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim registration = service.CreateIncrementalAnalyzer(workspace) @@ -376,7 +372,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim waiter = New AsynchronousOperationListener() Dim project = workspace.CurrentSolution.Projects.First() - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim service = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim registration = service.CreateIncrementalAnalyzer(workspace) @@ -452,7 +447,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim service = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim registration = service.CreateIncrementalAnalyzer(workspace) Using source = New ExternalErrorDiagnosticUpdateSource( @@ -472,13 +466,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics location:=New DiagnosticDataLocation(New FileLinePositionSpan("Test.txt", New LinePosition(4, 4), New LinePosition(4, 4)), documentId:=Nothing), language:=project.Language) - AddHandler service.DiagnosticsUpdated, Sub(o, argsCollection) - Dim args = argsCollection.Single() - Dim diagnostics = args.Diagnostics + 'AddHandler service.DiagnosticsUpdated, Sub(o, argsCollection) + ' Dim args = argsCollection.Single() + ' Dim diagnostics = args.Diagnostics - Assert.Single(diagnostics) - Assert.Equal(diagnostics(0).Id, diagnostic.Id) - End Sub + ' Assert.Single(diagnostics) + ' Assert.Equal(diagnostics(0).Id, diagnostic.Id) + ' End Sub source.AddNewErrors(project.Id, diagnostic) Await waiter.ExpeditedWaitAsync() @@ -508,7 +502,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim project = workspace.CurrentSolution.Projects.First() Dim document = project.Documents.Single() - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim service = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) Dim registration = service.CreateIncrementalAnalyzer(workspace) Using source = New ExternalErrorDiagnosticUpdateSource( @@ -637,7 +630,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics End Function Private Class TestDiagnosticAnalyzerService - Implements IDiagnosticAnalyzerService, IDiagnosticUpdateSource + Implements IDiagnosticAnalyzerService Private ReadOnly _analyzerInfoCache As DiagnosticAnalyzerInfoCache @@ -654,10 +647,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics End Get End Property - Public Event DiagnosticsUpdated As EventHandler(Of ImmutableArray(Of DiagnosticsUpdatedArgs)) Implements IDiagnosticUpdateSource.DiagnosticsUpdated - Public Event DiagnosticsCleared As EventHandler Implements IDiagnosticUpdateSource.DiagnosticsCleared - - Public Sub Reanalyze(workspace As Workspace, projectIds As IEnumerable(Of ProjectId), documentIds As IEnumerable(Of DocumentId), highPriority As Boolean) Implements IDiagnosticAnalyzerService.Reanalyze + Public Sub RequestDiagnosticRefresh() Implements IDiagnosticAnalyzerService.RequestDiagnosticRefresh End Sub Public Function GetDiagnosticsForSpanAsync(document As TextDocument, range As TextSpan?, shouldIncludeDiagnostic As Func(Of String, Boolean), includeCompilerDiagnostics As Boolean, includeSuppressedDiagnostics As Boolean, priority As ICodeActionRequestPriorityProvider, addOperationScope As Func(Of String, IDisposable), diagnosticKinds As DiagnosticKind, isExplicit As Boolean, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of DiagnosticData)) Implements IDiagnosticAnalyzerService.GetDiagnosticsForSpanAsync diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb index d22fe45fbdd0d..8e4c35d039482 100644 --- a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioAnalyzerTests.vb @@ -21,22 +21,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim Public Class VisualStudioAnalyzerTests - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Public Sub GetReferenceCalledMultipleTimes() Using workspace = New TestWorkspace(composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) - Dim lazyWorkspace = New Lazy(Of VisualStudioWorkspace)( - Function() - Return Nothing - End Function) - - Dim registrationService = Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) - Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(lazyWorkspace, registrationService) - - Using tempRoot = New TempRoot(), analyzer = New ProjectAnalyzerReference(tempRoot.CreateFile().Path, hostDiagnosticUpdateSource, ProjectId.CreateNewId(), LanguageNames.VisualBasic) + Using tempRoot = New TempRoot(), analyzer = New ProjectAnalyzerReference(tempRoot.CreateFile().Path, HostDiagnosticUpdateSource.Instance, ProjectId.CreateNewId(), LanguageNames.VisualBasic) Dim reference1 = analyzer.GetReference() Dim reference2 = analyzer.GetReference() @@ -45,34 +35,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim End Using End Sub - - Public Sub AnalyzerErrorsAreUpdated() - Using workspace = New TestWorkspace(composition:=s_compositionWithMockDiagnosticUpdateSourceRegistrationService) - Dim lazyWorkspace = New Lazy(Of VisualStudioWorkspace)( - Function() - Return Nothing - End Function) - - Dim file = Path.GetTempFileName() - - Dim registrationService = Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) - Dim hostDiagnosticUpdateSource = New HostDiagnosticUpdateSource(lazyWorkspace, registrationService) - - Dim eventHandler = New EventHandlers(file) - AddHandler hostDiagnosticUpdateSource.DiagnosticsUpdated, AddressOf eventHandler.DiagnosticAddedTest - - Using analyzer = New ProjectAnalyzerReference(file, hostDiagnosticUpdateSource, ProjectId.CreateNewId(), LanguageNames.VisualBasic) - Dim reference = analyzer.GetReference() - reference.GetAnalyzers(LanguageNames.VisualBasic) - - RemoveHandler hostDiagnosticUpdateSource.DiagnosticsUpdated, AddressOf eventHandler.DiagnosticAddedTest - AddHandler hostDiagnosticUpdateSource.DiagnosticsUpdated, AddressOf EventHandlers.DiagnosticRemovedTest - End Using - - IO.File.Delete(file) - End Using - End Sub - Private Class EventHandlers Public File As String diff --git a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index 39fa9362956d1..0f6d62a5a75da 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -38,9 +38,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus Public Class DocumentService_IntegrationTests - Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ - .AddParts(GetType(MockDiagnosticUpdateSourceRegistrationService)) + Private Shared ReadOnly s_compositionWithMockDiagnosticUpdateSourceRegistrationService As TestComposition = EditorTestCompositions.EditorFeatures Public Async Function TestFindUsageIntegration() As System.Threading.Tasks.Task @@ -231,7 +229,6 @@ class { } ' confirm there are errors Assert.True(model.GetDiagnostics().Any()) - Assert.IsType(Of MockDiagnosticUpdateSourceRegistrationService)(workspace.GetService(Of IDiagnosticUpdateSourceRegistrationService)()) Dim diagnosticService = Assert.IsType(Of DiagnosticAnalyzerService)(workspace.GetService(Of IDiagnosticAnalyzerService)()) ' confirm diagnostic support is off for the document diff --git a/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs b/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs index 96571de4a4b48..e9cb2154246b9 100644 --- a/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs +++ b/src/VisualStudio/DevKit/Impl/EditAndContinue/ManagedHotReloadLanguageServiceBridge.cs @@ -4,10 +4,8 @@ using System; using System.ComponentModel.Composition; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.BrokeredServices; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; @@ -16,22 +14,13 @@ namespace Microsoft.CodeAnalysis.EditAndContinue; -[ExportBrokeredService(MonikerName, ServiceVersion, Audience = ServiceAudience.Local)] +[ExportBrokeredService(ManagedHotReloadLanguageServiceDescriptor.MonikerName, ManagedHotReloadLanguageServiceDescriptor.ServiceVersion, Audience = ServiceAudience.Local)] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed partial class ManagedHotReloadLanguageServiceBridge(InternalContracts.IManagedHotReloadLanguageService service) : IManagedHotReloadLanguageService, IExportedBrokeredService { - private const string ServiceName = "ManagedHotReloadLanguageService"; - private const string ServiceVersion = "0.1"; - private const string MonikerName = BrokeredServiceDescriptors.LanguageServerComponentNamespace + "." + BrokeredServiceDescriptors.LanguageServerComponentName + "." + ServiceName; - - public static readonly ServiceJsonRpcDescriptor ServiceDescriptor = BrokeredServiceDescriptors.CreateServerServiceDescriptor(ServiceName, new(ServiceVersion)); - - static ManagedHotReloadLanguageServiceBridge() - => Debug.Assert(ServiceDescriptor.Moniker.Name == MonikerName); - ServiceRpcDescriptor IExportedBrokeredService.Descriptor - => ServiceDescriptor; + => ManagedHotReloadLanguageServiceDescriptor.Descriptor; public Task InitializeAsync(CancellationToken cancellationToken) => Task.CompletedTask; diff --git a/src/VisualStudio/IntegrationTest/IntegrationTestBuildProject.csproj b/src/VisualStudio/IntegrationTest/IntegrationTestBuildProject.csproj index cdd4ca9bcbd6b..fd37008e6ce7e 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTestBuildProject.csproj +++ b/src/VisualStudio/IntegrationTest/IntegrationTestBuildProject.csproj @@ -13,7 +13,6 @@ - diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicClassification.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicClassification.cs index 73dd8249af105..0585788a411e6 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicClassification.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicClassification.cs @@ -43,7 +43,7 @@ End Class End Namespace", HangMitigatingCancellationToken); await TestServices.Editor.PlaceCaretAsync("MathAlias", charsOffset: 0, HangMitigatingCancellationToken); - await TestServices.EditorVerifier.CurrentTokenTypeAsync(tokenType: "identifier", HangMitigatingCancellationToken); + await TestServices.EditorVerifier.CurrentTokenTypeAsync(tokenType: "class name", HangMitigatingCancellationToken); await TestServices.Editor.PlaceCaretAsync("Namespace", charsOffset: 0, HangMitigatingCancellationToken); await TestServices.EditorVerifier.CurrentTokenTypeAsync(tokenType: "keyword", HangMitigatingCancellationToken); await TestServices.Editor.PlaceCaretAsync("summary", charsOffset: 0, HangMitigatingCancellationToken); diff --git a/src/VisualStudio/Setup.ServiceHub/Directory.Build.props b/src/VisualStudio/Setup.ServiceHub/Directory.Build.props index 97c5e74599af2..4dff2f71e7b9d 100644 --- a/src/VisualStudio/Setup.ServiceHub/Directory.Build.props +++ b/src/VisualStudio/Setup.ServiceHub/Directory.Build.props @@ -24,7 +24,6 @@ .\ - core diff --git a/src/VisualStudio/Setup/Directory.Build.targets b/src/VisualStudio/Setup/Directory.Build.targets index f3be81af53814..e70ab346f5b09 100644 --- a/src/VisualStudio/Setup/Directory.Build.targets +++ b/src/VisualStudio/Setup/Directory.Build.targets @@ -2,5 +2,4 @@ - \ No newline at end of file diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index 5183dad27cba1..5d2ff4a4c435a 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -19,12 +19,6 @@ Microsoft.CodeAnalysis.LanguageServices - - - - .\ - desktop @@ -328,6 +322,19 @@ PublishProjectOutputGroup + + SemanticSearchRefs + + TargetFramework=$(NetRoslyn) + true + false + + false + SemanticSearchRefs + PublishProjectOutputGroup + + false + diff --git a/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb b/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb index fd973ec00660e..8a25462f33b34 100644 --- a/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb +++ b/src/VisualStudio/TestUtilities2/ProjectSystemShim/Framework/TestEnvironment.vb @@ -50,7 +50,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr ' GetType(MockWorkspaceEventListenerProvider)) Private Shared ReadOnly s_composition As TestComposition = EditorTestCompositions.EditorFeaturesWpf _ - .AddExcludedPartTypes(GetType(IDiagnosticUpdateSourceRegistrationService)) _ .AddParts( GetType(FileChangeWatcherProvider), GetType(MockVisualStudioWorkspace), @@ -66,9 +65,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Fr GetType(VsMetadataServiceFactory), GetType(VisualStudioMetadataReferenceManagerFactory), GetType(MockWorkspaceEventListenerProvider), - GetType(HostDiagnosticUpdateSource), GetType(HierarchyItemToProjectIdMap), - GetType(DiagnosticService)) + GetType(DiagnosticAnalyzerService)) Private ReadOnly _workspace As VisualStudioWorkspaceImpl Private ReadOnly _projectFilePaths As New List(Of String) diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml index 4d762d6809e6f..9c2d01081864c 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml @@ -53,13 +53,7 @@ - - - + Content="{x:Static local:AdvancedOptionPageStrings.Option_run_code_analysis_in_separate_process}" /> @@ -175,6 +169,8 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Show_guides_for_declaration_level_constructs}" /> + @@ -205,6 +201,8 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Report_invalid_placeholders_in_string_dot_format_calls}" /> + diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index 899bb86301916..077426ac2c570 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -71,7 +71,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(at_the_end_of_the_line_of_code, InlineDiagnosticsOptionsStorage.Location, InlineDiagnosticsLocations.PlacedAtEndOfCode, LanguageNames.VisualBasic) BindToOption(on_the_right_edge_of_the_editor_window, InlineDiagnosticsOptionsStorage.Location, InlineDiagnosticsLocations.PlacedAtEndOfEditor, LanguageNames.VisualBasic) BindToOption(Run_code_analysis_in_separate_process, RemoteHostOptionsStorage.OOP64Bit) - BindToOption(Run_code_analysis_on_dotnet, RemoteHostOptionsStorage.OOPCoreClr) BindToOption(Enable_file_logging_for_diagnostics, VisualStudioLoggingOptionsStorage.EnableFileLoggingForDiagnostics) BindToOption(Skip_analyzers_for_implicitly_triggered_builds, FeatureOnOffOptions.SkipAnalyzersForImplicitlyTriggeredBuilds) @@ -114,6 +113,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options ' Block structure guides BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.VisualBasic) BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.VisualBasic) + BindToOption(Show_guides_for_comments_and_preprocessor_regions, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, LanguageNames.VisualBasic) ' Comments BindToOption(GenerateXmlDocCommentsForTripleApostrophes, DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, LanguageNames.VisualBasic) @@ -127,6 +127,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(ShowRemarksInQuickInfo, QuickInfoOptionsStorage.ShowRemarksInQuickInfo, LanguageNames.VisualBasic) BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, IdeAnalyzerOptionsStorage.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.VisualBasic) BindToOption(Underline_reassigned_variables, ClassificationOptionsStorage.ClassifyReassignedVariables, LanguageNames.VisualBasic) + BindToOption(Strike_out_obsolete_symbols, ClassificationOptionsStorage.ClassifyObsoleteSymbols, LanguageNames.VisualBasic) ' Go To Definition BindToOption(NavigateToObjectBrowser, VisualStudioNavigationOptionsStorage.NavigateToObjectBrowser, LanguageNames.VisualBasic) @@ -241,13 +242,5 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Collapse_sourcelink_embedded_decompiled_files_on_open.IsEnabled = False Collapse_metadata_signature_files_on_open.IsEnabled = False End Sub - - Private Sub Run_code_analysis_in_separate_process_Checked(sender As Object, e As RoutedEventArgs) - Run_code_analysis_on_dotnet.IsEnabled = True - End Sub - - Private Sub Run_code_analysis_in_separate_process_Unchecked(sender As Object, e As RoutedEventArgs) - Run_code_analysis_on_dotnet.IsEnabled = False - End Sub End Class End Namespace diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb index 545513dcf7094..14d6aed27a792 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb @@ -76,15 +76,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Public ReadOnly Property Option_run_code_analysis_in_separate_process As String = ServicesVSResources.Run_code_analysis_in_separate_process_requires_restart - Public ReadOnly Property Option_run_code_analysis_on_dotnet As String = - ServicesVSResources.Run_code_analysis_on_latest_dotnet_requires_restart - Public ReadOnly Property Option_DisplayLineSeparators As String = BasicVSResources.Show_procedure_line_separators Public ReadOnly Property Option_Underline_reassigned_variables As String = ServicesVSResources.Underline_reassigned_variables + Public ReadOnly Property Option_Strike_out_obsolete_symbols As String = + ServicesVSResources.Strike_out_obsolete_symbols + Public ReadOnly Property Option_Display_all_hints_while_pressing_Alt_F1 As String = ServicesVSResources.Display_all_hints_while_pressing_Alt_F1 @@ -258,6 +258,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Public ReadOnly Property Option_Show_guides_for_code_level_constructs As String = ServicesVSResources.Show_guides_for_code_level_constructs + Public ReadOnly Property Option_Show_guides_for_comments_and_preprocessor_regions As String = + ServicesVSResources.Show_guides_for_comments_and_preprocessor_regions + Public ReadOnly Property Option_Fading As String = ServicesVSResources.Fading diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 4b687b6a306e8..9f99d822e1cd4 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -672,7 +672,9 @@ private static AccessorDeclarationSyntax WithBody(AccessorDeclarationSyntax acce => accessorList?.WithAccessors([.. accessorList.Accessors.Select(WithoutBody)]); private static AccessorDeclarationSyntax WithoutBody(AccessorDeclarationSyntax accessor) - => accessor.Body != null ? accessor.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)).WithBody(null) : accessor; + => accessor.Body != null ? accessor.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)).WithBody(null) + : accessor.ExpressionBody != null ? accessor.WithExpressionBody(null) + : accessor; private protected override SyntaxNode ClassDeclaration( bool isRecord, @@ -800,7 +802,8 @@ internal override SyntaxNode AsInterfaceMember(SyntaxNode m) return ((MethodDeclarationSyntax)member) .WithModifiers(default) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)) - .WithBody(null); + .WithBody(null) + .WithExpressionBody(null); case SyntaxKind.OperatorDeclaration: var operatorDeclaration = (OperatorDeclarationSyntax)member; @@ -1200,6 +1203,9 @@ private static SyntaxNode WithAttributeLists(SyntaxNode declaration, SyntaxList< _ => declaration, }; + internal override SyntaxNode? GetPrimaryConstructorParameterList(SyntaxNode declaration) + => declaration is TypeDeclarationSyntax { ParameterList: { } parameterList } ? parameterList : null; + internal override ImmutableArray GetTypeInheritance(SyntaxNode declaration) => declaration is BaseTypeDeclarationSyntax baseType && baseType.BaseList != null ? [baseType.BaseList] diff --git a/src/Workspaces/CSharp/Portable/ObsoleteSymbol/CSharpObsoleteSymbolService.cs b/src/Workspaces/CSharp/Portable/ObsoleteSymbol/CSharpObsoleteSymbolService.cs new file mode 100644 index 0000000000000..db62ff996c5f7 --- /dev/null +++ b/src/Workspaces/CSharp/Portable/ObsoleteSymbol/CSharpObsoleteSymbolService.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.ObsoleteSymbol; + +namespace Microsoft.CodeAnalysis.CSharp.ObsoleteSymbol; + +[ExportLanguageService(typeof(IObsoleteSymbolService), LanguageNames.CSharp)] +[Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpObsoleteSymbolService() : AbstractObsoleteSymbolService(dimKeywordKind: null) +{ +} diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs index d5e06181cc09c..29e15cbaddf39 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs @@ -2781,6 +2781,45 @@ interface i """); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65932")] + public void TestAddExpressionBodyMembersToInterface() + { + var method = (MethodDeclarationSyntax)Generator.MethodDeclaration("m"); + method = method.WithBody(null).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + method = method.WithExpressionBody(SyntaxFactory.ArrowExpressionClause((ExpressionSyntax)Generator.IdentifierName("x"))); + + VerifySyntax(Generator.AddMembers(Generator.InterfaceDeclaration("i"), + [method]), + """ + interface i + { + void m(); + } + """); + + var getAccessor = (AccessorDeclarationSyntax)Generator.GetAccessorDeclaration(); + getAccessor = getAccessor.WithBody(null).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + getAccessor = getAccessor.WithExpressionBody(SyntaxFactory.ArrowExpressionClause((ExpressionSyntax)Generator.IdentifierName("x"))); + + var setAccessor = (AccessorDeclarationSyntax)Generator.SetAccessorDeclaration(); + setAccessor = setAccessor.WithBody(null).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); + setAccessor = setAccessor.WithExpressionBody(SyntaxFactory.ArrowExpressionClause((ExpressionSyntax)Generator.InvocationExpression(Generator.IdentifierName("x")))); + + var property = (PropertyDeclarationSyntax) + Generator.WithAccessorDeclarations( + Generator.PropertyDeclaration("p", Generator.IdentifierName("x")), + [getAccessor, setAccessor]); + + VerifySyntax(Generator.AddMembers(Generator.InterfaceDeclaration("i"), + [property]), + """ + interface i + { + x p { get; set; } + } + """); + } + [Fact] public void TestRemoveMembers() { diff --git a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj index 98ad23cbaab6d..6fb4366c78c83 100644 --- a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj +++ b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj @@ -112,8 +112,8 @@ Similarly, we set CopyToOutputDirectory for the regular build and PackageCopyToPutput for the packaging process. --> diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.cs.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.cs.xlf index 9f4793e6c3bc1..a2069b38f5d33 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.cs.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.cs.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + V {0} se nepovedlo najít hostitele sestavení. diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.de.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.de.xlf index 81c26ae58434b..6125fa5918100 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.de.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.de.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + Der Buildhost wurde unter „{0}“ nicht gefunden. diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.es.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.es.xlf index 1991d2eb1855e..9b38ece6f434a 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.es.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.es.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + No se encontró el host de compilación en '{0}' diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.fr.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.fr.xlf index f989e4b3ae526..1ea565215356e 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.fr.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.fr.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + Désolé... Nous n’avons pas pu trouver l’hôte de build sur « {0} » diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.it.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.it.xlf index 605413f8a060b..4996c28f4ab66 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.it.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.it.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + Non è stato possibile trovare l'host di compilazione in '{0}' diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ja.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ja.xlf index 95eb800dbfb67..6813cc30aeb2e 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ja.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ja.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + ビルド ホストが '{0}' で見つかりませんでした diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ko.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ko.xlf index 0fe8f7fdee94f..88c99e8b9aa5c 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ko.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ko.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + '{0}'에서 빌드 호스트를 찾을 수 없습니다. diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pl.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pl.xlf index 6dc43d8237197..a46d4b3f5b141 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pl.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pl.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + Nie można odnaleźć hosta kompilacji w lokalizacji „{0}” diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pt-BR.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pt-BR.xlf index 6bb7f41a3d416..edf0d856b64b9 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pt-BR.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.pt-BR.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + Não foi possível encontrar o host do build em "{0}" diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ru.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ru.xlf index 9bf6cc048a802..1380789379b15 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ru.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.ru.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + Не удалось найти хост сборки в "{0}" diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.tr.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.tr.xlf index 043e0a112b301..3c9da2a38945e 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.tr.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.tr.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + '{0}' konumunda derleme konağı bulunamadı diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hans.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hans.xlf index 36348690a317d..e0f9b88692ee0 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hans.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hans.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + 在“{0}”中找不到生成主机 diff --git a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hant.xlf b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hant.xlf index 642d6583b9f4d..36b7d71732fb7 100644 --- a/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hant.xlf +++ b/src/Workspaces/Core/MSBuild/xlf/WorkspaceMSBuildResources.zh-Hant.xlf @@ -64,7 +64,7 @@ The build host could not be found at '{0}' - The build host could not be found at '{0}' + 在 '{0}' 找不到組建主機 diff --git a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs index f112e3dbb5cf9..176d0ebd7aac7 100644 --- a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.ObsoleteSymbol; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.ReassignedVariable; using Microsoft.CodeAnalysis.Remote; @@ -139,6 +140,7 @@ public static async Task AddClassificationsInCurrentProcessAsync( { var classificationService = document.GetRequiredLanguageService(); var reassignedVariableService = document.GetRequiredLanguageService(); + var obsoleteSymbolService = document.GetRequiredLanguageService(); var extensionManager = document.Project.Solution.Services.GetRequiredService(); var classifiers = classificationService.GetDefaultSyntaxClassifiers(); @@ -155,6 +157,13 @@ await classificationService.AddSemanticClassificationsAsync( foreach (var span in reassignedVariableSpans) result.Add(new ClassifiedSpan(span, ClassificationTypeNames.ReassignedVariable)); } + + if (options.ClassifyObsoleteSymbols) + { + var obsoleteSymbolSpans = await obsoleteSymbolService.GetLocationsAsync(document, textSpans, cancellationToken).ConfigureAwait(false); + foreach (var span in obsoleteSymbolSpans) + result.Add(new ClassifiedSpan(span, ClassificationTypeNames.ObsoleteSymbol)); + } } else if (type == ClassificationType.EmbeddedLanguage) { diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs b/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs index ef812b4362d53..417ec439686f0 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs @@ -10,6 +10,7 @@ namespace Microsoft.CodeAnalysis.Classification; internal readonly record struct ClassificationOptions { [DataMember] public bool ClassifyReassignedVariables { get; init; } = false; + [DataMember] public bool ClassifyObsoleteSymbols { get; init; } = true; [DataMember] public bool ColorizeRegexPatterns { get; init; } = true; [DataMember] public bool ColorizeJsonPatterns { get; init; } = true; [DataMember] public bool ForceFrozenPartialSemanticsForCrossProcessOperations { get; init; } = false; diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs index 27bce3937550a..e8a52b10f219f 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassificationTypeNames.cs @@ -11,7 +11,7 @@ public static class ClassificationTypeNames /// /// Additive classifications types supply additional context to other classifications. /// - public static ImmutableArray AdditiveTypeNames { get; } = [StaticSymbol, ReassignedVariable, TestCode]; + public static ImmutableArray AdditiveTypeNames { get; } = [StaticSymbol, ReassignedVariable, ObsoleteSymbol, TestCode]; public static ImmutableArray AllTypeNames { get; } = [ @@ -28,6 +28,7 @@ public static class ClassificationTypeNames WhiteSpace, Text, ReassignedVariable, + ObsoleteSymbol, StaticSymbol, PreprocessorText, Punctuation, @@ -112,6 +113,7 @@ public static class ClassificationTypeNames public const string Text = "text"; internal const string ReassignedVariable = "reassigned variable"; + internal const string ObsoleteSymbol = "obsolete symbol"; public const string StaticSymbol = "static symbol"; public const string PreprocessorText = "preprocessor text"; diff --git a/src/Workspaces/Core/Portable/Classification/Classifier.cs b/src/Workspaces/Core/Portable/Classification/Classifier.cs index 7a4fdb6b15726..83cbbd070f4a5 100644 --- a/src/Workspaces/Core/Portable/Classification/Classifier.cs +++ b/src/Workspaces/Core/Portable/Classification/Classifier.cs @@ -77,7 +77,6 @@ internal static IEnumerable GetClassifiedSpans( { var projectServices = services.GetLanguageServices(semanticModel.Language); var classificationService = projectServices.GetRequiredService(); - var embeddedLanguageService = projectServices.GetRequiredService(); var syntaxClassifiers = classificationService.GetDefaultSyntaxClassifiers(); @@ -94,8 +93,12 @@ internal static IEnumerable GetClassifiedSpans( classificationService.AddSemanticClassifications(semanticModel, textSpan, getNodeClassifiers, getTokenClassifiers, semanticClassifications, options, cancellationToken); // intentionally adding to the semanticClassifications array here. - if (includedEmbeddedClassifications && project != null) + if (includedEmbeddedClassifications + && project != null + && projectServices.GetService() is { } embeddedLanguageService) + { embeddedLanguageService.AddEmbeddedLanguageClassifications(services, project, semanticModel, textSpan, options, semanticClassifications, cancellationToken); + } var allClassifications = new List(semanticClassifications.Where(s => s.TextSpan.OverlapsWith(textSpan))); var semanticSet = semanticClassifications.Select(s => s.TextSpan).ToSet(); diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index bbff16f5dc05b..8599d0536cea8 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -221,29 +221,21 @@ public ImmutableArray GetAllDiagnostics() Contract.ThrowIfNull(_nonLocals); Contract.ThrowIfTrue(_others.IsDefault); - var builder = ArrayBuilder.GetInstance(); + using var _ = ArrayBuilder.GetInstance(out var builder); foreach (var data in _syntaxLocals.Values) - { builder.AddRange(data); - } foreach (var data in _semanticLocals.Values) - { builder.AddRange(data); - } foreach (var data in _nonLocals.Values) - { builder.AddRange(data); - } foreach (var data in _others) - { builder.AddRange(data); - } - return builder.ToImmutableAndFree(); + return builder.ToImmutable(); } public ImmutableArray GetDocumentDiagnostics(DocumentId documentId, AnalysisKind kind) diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs b/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs index 51c79506734fd..c7111748afc1f 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs @@ -75,4 +75,7 @@ public static void AddInterfaceType(this SyntaxEditor editor, SyntaxNode declara public static void AddBaseType(this SyntaxEditor editor, SyntaxNode declaration, SyntaxNode baseType) => editor.ReplaceNode(declaration, (d, g) => g.AddBaseType(d, baseType)); + + internal static void RemovePrimaryConstructor(this SyntaxEditor editor, SyntaxNode declaration) + => editor.ReplaceNode(declaration, (d, g) => g.RemovePrimaryConstructor(d)); } diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index e26b4065bb6cf..427d9f3535086 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -1071,6 +1071,14 @@ public SyntaxNode RemoveAllAttributes(SyntaxNode declaration) /// internal abstract SyntaxNode RemoveAllComments(SyntaxNode node); + internal SyntaxNode RemovePrimaryConstructor(SyntaxNode declaration) + { + var node = GetPrimaryConstructorParameterList(declaration); + return RemoveNodes(declaration, node is not null ? [node] : []); + } + + internal abstract SyntaxNode? GetPrimaryConstructorParameterList(SyntaxNode declaration); + internal SyntaxNode RemoveLeadingAndTrailingComments(SyntaxNode node) { return node.WithLeadingTrivia(RemoveCommentLines(node.GetLeadingTrivia())) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs index 6a949418c1c15..900771145172e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchOptions.cs @@ -53,13 +53,18 @@ namespace Microsoft.CodeAnalysis.FindSymbols; /// option is the more relevant with knowing if a particular reference would actually result in a call to the /// original member, not if it has a relation to the original member. /// +/// +/// Displays all definitions regardless of whether they have a reference or not. +/// /// + [DataContract] internal readonly record struct FindReferencesSearchOptions( [property: DataMember(Order = 0)] bool AssociatePropertyReferencesWithSpecificAccessor = false, [property: DataMember(Order = 1)] bool Cascade = true, [property: DataMember(Order = 2)] bool Explicit = true, - [property: DataMember(Order = 3)] bool UnidirectionalHierarchyCascade = false) + [property: DataMember(Order = 3)] bool UnidirectionalHierarchyCascade = false, + [property: DataMember(Order = 4)] bool DisplayAllDefinitions = false) { public FindReferencesSearchOptions() : this(AssociatePropertyReferencesWithSpecificAccessor: false) diff --git a/src/Workspaces/Core/Portable/Log/PiiValue.cs b/src/Workspaces/Core/Portable/Log/PiiValue.cs index 667143b2ff4da..720d8afb54efb 100644 --- a/src/Workspaces/Core/Portable/Log/PiiValue.cs +++ b/src/Workspaces/Core/Portable/Log/PiiValue.cs @@ -2,16 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.Linq; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.Internal.Log; /// /// Represents telemetry data that's classified as personally identifiable information. /// +[DataContract] internal sealed class PiiValue(object value) { + [DataMember(Order = 0)] public readonly object Value = value; public override string? ToString() diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 556fb9fb07110..3c1b1a4561ae0 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -134,6 +134,7 @@ + diff --git a/src/Workspaces/Core/Portable/ObsoleteSymbol/AbstractObsoleteSymbolService.cs b/src/Workspaces/Core/Portable/ObsoleteSymbol/AbstractObsoleteSymbolService.cs new file mode 100644 index 0000000000000..d3349b8b75c30 --- /dev/null +++ b/src/Workspaces/Core/Portable/ObsoleteSymbol/AbstractObsoleteSymbolService.cs @@ -0,0 +1,218 @@ +// Licensed to the .NET Foundation under one or more 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.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ObsoleteSymbol; + +internal abstract class AbstractObsoleteSymbolService(int? dimKeywordKind) : IObsoleteSymbolService +{ + /// + /// The of the keyword in Visual Basic, or + /// for C# scenarios. This value is used to improve performance in the token classification + /// fast-path by avoiding unnecessary calls to . + /// + private readonly int? _dimKeywordKind = dimKeywordKind; + + protected virtual void ProcessDimKeyword(ref ArrayBuilder? result, SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken) + { + // Take no action by default + } + + public async Task> GetLocationsAsync(Document document, ImmutableArray textSpans, CancellationToken cancellationToken) + { + var syntaxFacts = document.GetRequiredLanguageService(); + + var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + // Avoid taking a builder from the pool in the common case where there are no references to obsolete symbols + // currently on screen. + ArrayBuilder? result = null; + try + { + var semanticModel = compilation.GetSemanticModel(root.SyntaxTree); + + foreach (var span in textSpans) + { + Recurse(span, semanticModel); + } + + if (result is null) + return ImmutableArray.Empty; + + result.RemoveDuplicates(); + return result.ToImmutableAndClear(); + } + finally + { + result?.Free(); + } + + void Recurse(TextSpan span, SemanticModel semanticModel) + { + using var _ = ArrayBuilder.GetInstance(out var stack); + + // Walk through all the nodes in the provided span. Directly analyze local or parameter declaration. And + // also analyze any identifiers which might be reference to locals or parameters. Note that we might hit + // locals/parameters without any references in the span, or references that don't have the declarations in + // the span + stack.Add(root.FindNode(span)); + + // Use a stack so we don't blow out the stack with recursion. + while (stack.Count > 0) + { + var current = stack.Last(); + stack.RemoveLast(); + + if (current.Span.IntersectsWith(span)) + { + var tokenFromNode = ProcessNode(semanticModel, current); + + foreach (var child in current.ChildNodesAndTokens()) + { + if (child.IsNode) + { + stack.Add(child.AsNode()!); + } + + var token = child.AsToken(); + if (token != tokenFromNode) + ProcessToken(semanticModel, child.AsToken()); + + ExtractStructureFromTrivia(stack, token.LeadingTrivia); + ExtractStructureFromTrivia(stack, token.TrailingTrivia); + } + } + } + } + + static void ExtractStructureFromTrivia(ArrayBuilder stack, SyntaxTriviaList triviaList) + { + foreach (var trivia in triviaList) + { + if (trivia.HasStructure) + { + stack.Add(trivia.GetStructure()!); + } + } + } + + void AddResult(TextSpan span) + { + result ??= ArrayBuilder.GetInstance(); + result.Add(span); + } + + SyntaxToken ProcessNode(SemanticModel semanticModel, SyntaxNode node) + { + if (syntaxFacts.IsUsingAliasDirective(node)) + { + syntaxFacts.GetPartsOfUsingAliasDirective(node, out _, out var aliasToken, out var name); + if (!aliasToken.Span.IsEmpty) + { + // Use 'name.Parent' because VB can't resolve the declared symbol directly from 'node' + var symbol = semanticModel.GetDeclaredSymbol(name.GetRequiredParent(), cancellationToken); + if (IsSymbolObsolete(symbol)) + AddResult(aliasToken.Span); + } + + return aliasToken; + } + else if (syntaxFacts.IsObjectCreationExpression(node)) + { + syntaxFacts.GetPartsOfObjectCreationExpression(node, out var creationKeyword, out _, out _, out _); + if (!creationKeyword.Span.IsEmpty) + { + // For syntax like the following + // + // SomeType value = new SomeType(); + // + // We classify 'new' as obsolete only if the specific constructor is obsolete. If the containing + // type is obsolete, the classification will be applied to 'SomeType' instead. + var symbol = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol; + if (IsSymbolObsolete(symbol)) + AddResult(creationKeyword.Span); + } + } + else if (syntaxFacts.IsImplicitObjectCreationExpression(node)) + { + syntaxFacts.GetPartsOfImplicitObjectCreationExpression(node, out var creationKeyword, out _, out _); + if (!creationKeyword.Span.IsEmpty) + { + // For syntax like the following + // + // SomeType value = new(); + // + // We classify 'new' as obsolete if either the type or the specific constructor is obsolete. + var symbol = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol; + if (IsSymbolObsolete(symbol) || IsSymbolObsolete(symbol?.ContainingType)) + AddResult(creationKeyword.Span); + } + } + + return default; + } + + void ProcessToken(SemanticModel semanticModel, SyntaxToken token) + { + if (syntaxFacts.IsIdentifier(token)) + { + ProcessIdentifier(semanticModel, token); + } + else if (token.RawKind == _dimKeywordKind) + { + ProcessDimKeyword(ref result, semanticModel, token, cancellationToken); + } + } + + void ProcessIdentifier(SemanticModel semanticModel, SyntaxToken token) + { + if (syntaxFacts.IsDeclaration(token.Parent)) + { + var symbol = semanticModel.GetDeclaredSymbol(token.Parent, cancellationToken); + if (IsSymbolObsolete(symbol)) + AddResult(token.Span); + } + else + { + var symbol = semanticModel.GetSymbolInfo(token, cancellationToken).Symbol; + if (IsSymbolObsolete(symbol)) + AddResult(token.Span); + } + } + } + + protected static bool IsSymbolObsolete([NotNullWhen(true)] ISymbol? symbol) + { + // Avoid infinite recursion. Iteration limit chosen arbitrarily; cases are generally expected to complete on + // the first iteration or fail completely. + for (var i = 0; i < 5; i++) + { + if (symbol is IAliasSymbol alias) + { + symbol = alias.Target; + continue; + } + + if (symbol is INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T, TypeArguments: [var valueType] }) + { + symbol = valueType; + continue; + } + + return symbol?.IsObsolete() ?? false; + } + + // Unable to determine whether the symbol is considered obsolete + return false; + } +} diff --git a/src/Workspaces/Core/Portable/ObsoleteSymbol/IObsoleteSymbolService.cs b/src/Workspaces/Core/Portable/ObsoleteSymbol/IObsoleteSymbolService.cs new file mode 100644 index 0000000000000..7e1d65363baff --- /dev/null +++ b/src/Workspaces/Core/Portable/ObsoleteSymbol/IObsoleteSymbolService.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ObsoleteSymbol; + +/// +/// Service which can analyze a span of a document and identify all locations of declarations or references to +/// symbols which are marked . +/// +internal interface IObsoleteSymbolService : ILanguageService +{ + Task> GetLocationsAsync(Document document, ImmutableArray textSpans, CancellationToken cancellationToken); +} diff --git a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs index 5542cc7fbcb51..4f8bb3b9ddc17 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs @@ -151,6 +151,17 @@ public async ValueTask TryInvokeAsync( return await connection.TryInvokeAsync(project, invocation, cancellationToken).ConfigureAwait(false); } + public async ValueTask> TryInvokeAsync( + SolutionCompilationState compilationState, + ProjectId projectId, + Func> invocation, + CancellationToken cancellationToken) + where TService : class + { + using var connection = CreateConnection(callbackTarget: null); + return await connection.TryInvokeAsync(compilationState, projectId, invocation, cancellationToken).ConfigureAwait(false); + } + /// /// Equivalent to /// except that only the project (and its dependent projects) will be sync'ed to the remote host before executing. diff --git a/src/Workspaces/Core/Portable/Remote/WellKnownSynchronizationKind.cs b/src/Workspaces/Core/Portable/Remote/WellKnownSynchronizationKind.cs index 3b58dc9d74380..19f2efc2db2e2 100644 --- a/src/Workspaces/Core/Portable/Remote/WellKnownSynchronizationKind.cs +++ b/src/Workspaces/Core/Portable/Remote/WellKnownSynchronizationKind.cs @@ -28,7 +28,6 @@ internal enum WellKnownSynchronizationKind ProjectReference, MetadataReference, AnalyzerReference, - SourceText, SerializableSourceText, } diff --git a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs index 813c56037b114..00c816d4c0e31 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs @@ -5,16 +5,21 @@ using System; using System.Collections.Immutable; using System.Diagnostics; -using System.Text; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.Host.TemporaryStorageService; namespace Microsoft.CodeAnalysis.Serialization; +#pragma warning disable CA1416 // Validate platform compatibility + /// /// Represents a which can be serialized for sending to another process. The text is not /// required to be a live object in the current process, and can instead be held in temporary storage accessible by @@ -28,7 +33,7 @@ internal sealed class SerializableSourceText /// /// Exactly one of or will be non-. /// - private readonly ITemporaryTextStorageWithName? _storage; + private readonly TemporaryTextStorage? _storage; /// /// The in the current process. @@ -38,28 +43,40 @@ internal sealed class SerializableSourceText /// private readonly SourceText? _text; + /// + /// The hash that would be produced by calling on . Can be passed in when already known to avoid unnecessary computation costs. + /// + public readonly ImmutableArray ContentHash; + /// /// Weak reference to a SourceText computed from . Useful so that if multiple requests /// come in for the source text, the same one can be returned as long as something is holding it alive. /// private readonly WeakReference _computedText = new(target: null); - public SerializableSourceText(ITemporaryTextStorageWithName storage) - : this(storage, text: null) + public SerializableSourceText(TemporaryTextStorage storage, ImmutableArray contentHash) + : this(storage, text: null, contentHash) { } - public SerializableSourceText(SourceText text) - : this(storage: null, text) + public SerializableSourceText(SourceText text, ImmutableArray contentHash) + : this(storage: null, text, contentHash) { } - private SerializableSourceText(ITemporaryTextStorageWithName? storage, SourceText? text) + private SerializableSourceText(TemporaryTextStorage? storage, SourceText? text, ImmutableArray contentHash) { Debug.Assert(storage is null != text is null); _storage = storage; _text = text; + ContentHash = contentHash; + +#if DEBUG + var computedContentHash = TryGetText()?.GetContentHash() ?? _storage!.ContentHash; + Debug.Assert(contentHash.SequenceEqual(computedContentHash)); +#endif } /// @@ -70,11 +87,6 @@ private SerializableSourceText(ITemporaryTextStorageWithName? storage, SourceTex private SourceText? TryGetText() => _text ?? _computedText.GetTarget(); - public ImmutableArray GetContentHash() - { - return TryGetText()?.GetContentHash() ?? _storage!.GetContentHash(); - } - public async ValueTask GetTextAsync(CancellationToken cancellationToken) { var text = TryGetText(); @@ -99,17 +111,18 @@ public SourceText GetText(CancellationToken cancellationToken) return text; } - public static ValueTask FromTextDocumentStateAsync(TextDocumentState state, CancellationToken cancellationToken) + public static ValueTask FromTextDocumentStateAsync( + TextDocumentState state, CancellationToken cancellationToken) { - if (state.Storage is ITemporaryTextStorageWithName storage) + if (state.Storage is TemporaryTextStorage storage) { - return new ValueTask(new SerializableSourceText(storage)); + return new ValueTask(new SerializableSourceText(storage, storage.ContentHash)); } else { return SpecializedTasks.TransformWithoutIntermediateCancellationExceptionAsync( static (state, cancellationToken) => state.GetTextAsync(cancellationToken), - static (text, _) => new SerializableSourceText(text), + static (text, _) => new SerializableSourceText(text, text.GetContentHash()), state, cancellationToken); } @@ -124,6 +137,7 @@ public void Serialize(ObjectWriter writer, SolutionReplicationContext context, C writer.WriteInt32((int)_storage.ChecksumAlgorithm); writer.WriteEncoding(_storage.Encoding); + writer.WriteByteArray(ImmutableCollectionsMarshal.AsArray(_storage.ContentHash)!); writer.WriteInt32((int)SerializationKinds.MemoryMapFile); writer.WriteString(_storage.Name); @@ -136,6 +150,8 @@ public void Serialize(ObjectWriter writer, SolutionReplicationContext context, C writer.WriteInt32((int)_text.ChecksumAlgorithm); writer.WriteEncoding(_text.Encoding); + writer.WriteByteArray(ImmutableCollectionsMarshal.AsArray(_text.GetContentHash())!); + writer.WriteInt32((int)SerializationKinds.Bits); _text.WriteTo(writer, cancellationToken); } @@ -151,31 +167,27 @@ public static SerializableSourceText Deserialize( var checksumAlgorithm = (SourceHashAlgorithm)reader.ReadInt32(); var encoding = reader.ReadEncoding(); + var contentHash = ImmutableCollectionsMarshal.AsImmutableArray(reader.ReadByteArray()); var kind = (SerializationKinds)reader.ReadInt32(); Contract.ThrowIfFalse(kind is SerializationKinds.Bits or SerializationKinds.MemoryMapFile); if (kind == SerializationKinds.MemoryMapFile) { - var storage2 = (ITemporaryStorageService2)storageService; + var storage2 = (TemporaryStorageService)storageService; var name = reader.ReadRequiredString(); var offset = reader.ReadInt64(); var size = reader.ReadInt64(); - var storage = storage2.AttachTemporaryTextStorage(name, offset, size, checksumAlgorithm, encoding); - if (storage is ITemporaryTextStorageWithName storageWithName) - { - return new SerializableSourceText(storageWithName); - } - else - { - return new SerializableSourceText(storage.ReadText(cancellationToken)); - } + var storage = storage2.AttachTemporaryTextStorage(name, offset, size, checksumAlgorithm, encoding, contentHash); + return new SerializableSourceText(storage, contentHash); } else { - return new SerializableSourceText(SourceTextExtensions.ReadFrom(textService, reader, encoding, checksumAlgorithm, cancellationToken)); + return new SerializableSourceText( + SourceTextExtensions.ReadFrom(textService, reader, encoding, checksumAlgorithm, cancellationToken), + contentHash); } } } diff --git a/src/Workspaces/Core/Portable/Serialization/SerializationExtensions.cs b/src/Workspaces/Core/Portable/Serialization/SerializationExtensions.cs index 76aa8fd1e4438..eebea760eff75 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializationExtensions.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializationExtensions.cs @@ -30,7 +30,6 @@ public static WellKnownSynchronizationKind GetWellKnownSynchronizationKind(this MetadataReference => WellKnownSynchronizationKind.MetadataReference, AnalyzerReference => WellKnownSynchronizationKind.AnalyzerReference, SerializableSourceText => WellKnownSynchronizationKind.SerializableSourceText, - SourceText => WellKnownSynchronizationKind.SourceText, SourceGeneratedDocumentIdentity => WellKnownSynchronizationKind.SourceGeneratedDocumentIdentity, _ => throw ExceptionUtilities.UnexpectedValue(value), }; diff --git a/src/Workspaces/Core/Portable/Serialization/SerializerService.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService.cs index 33ce36e947f2a..e52f664c6694e 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService.cs @@ -84,10 +84,7 @@ public Checksum CreateChecksum(object value, CancellationToken cancellationToken return CreateChecksum((AnalyzerReference)value, cancellationToken); case WellKnownSynchronizationKind.SerializableSourceText: - return Checksum.Create(((SerializableSourceText)value).GetContentHash()); - - case WellKnownSynchronizationKind.SourceText: - return Checksum.Create(((SourceText)value).GetContentHash()); + return Checksum.Create(((SerializableSourceText)value).ContentHash); default: // object that is not part of solution is not supported since we don't know what inputs are required to @@ -148,10 +145,6 @@ public void Serialize(object value, ObjectWriter writer, SolutionReplicationCont SerializeSourceText((SerializableSourceText)value, writer, context, cancellationToken); return; - case WellKnownSynchronizationKind.SourceText: - SerializeSourceText(new SerializableSourceText((SourceText)value), writer, context, cancellationToken); - return; - case WellKnownSynchronizationKind.SolutionCompilationState: ((SolutionCompilationStateChecksums)value).Serialize(writer); return; @@ -203,7 +196,6 @@ public object Deserialize(WellKnownSynchronizationKind kind, ObjectReader reader WellKnownSynchronizationKind.MetadataReference => DeserializeMetadataReference(reader, cancellationToken), WellKnownSynchronizationKind.AnalyzerReference => DeserializeAnalyzerReference(reader, cancellationToken), WellKnownSynchronizationKind.SerializableSourceText => SerializableSourceText.Deserialize(reader, _storageService, _textService, cancellationToken), - WellKnownSynchronizationKind.SourceText => DeserializeSourceText(reader, cancellationToken), _ => throw ExceptionUtilities.UnexpectedValue(kind), }; } @@ -213,7 +205,7 @@ private IOptionsSerializationService GetOptionsSerializationService(string langu => _lazyLanguageSerializationService.GetOrAdd(languageName, n => _workspaceServices.GetLanguageServices(n).GetRequiredService()); public Checksum CreateParseOptionsChecksum(ParseOptions value) - => Checksum.Create(value, this); + => Checksum.Create((value, @this: this), static (tuple, writer) => tuple.@this.SerializeParseOptions(tuple.value, writer)); } // TODO: convert this to sub class rather than using enum with if statement. diff --git a/src/Workspaces/Core/Portable/Serialization/SerializerService_Asset.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService_Asset.cs index 9a59c289d4d95..c3842c9ff3870 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService_Asset.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService_Asset.cs @@ -22,12 +22,6 @@ private static void SerializeSourceText(SerializableSourceText text, ObjectWrite text.Serialize(writer, context, cancellationToken); } - private SourceText DeserializeSourceText(ObjectReader reader, CancellationToken cancellationToken) - { - var serializableSourceText = SerializableSourceText.Deserialize(reader, _storageService, _textService, cancellationToken); - return serializableSourceText.GetText(cancellationToken); - } - private void SerializeCompilationOptions(CompilationOptions options, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs index 9e2b09f3ee6cc..7face794e1425 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs @@ -472,13 +472,15 @@ private static ModuleMetadata ReadModuleMetadataFrom(ObjectReader reader, Serial } else { - var service2 = (ITemporaryStorageService2)_storageService; + var service2 = (TemporaryStorageService)_storageService; var name = reader.ReadRequiredString(); var offset = reader.ReadInt64(); var size = reader.ReadInt64(); +#pragma warning disable CA1416 // Validate platform compatibility var storage = service2.AttachTemporaryStreamStorage(name, offset, size); +#pragma warning restore CA1416 // Validate platform compatibility var length = size; return (storage, length); diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/IFindReferencesResultExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/IFindReferencesResultExtensions.cs index 2a464200b74a7..1865d376c4268 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/IFindReferencesResultExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/IFindReferencesResultExtensions.cs @@ -46,6 +46,11 @@ public static bool ShouldShow( public static bool ShouldShowWithNoReferenceLocations( this ISymbol definition, FindReferencesSearchOptions options, bool showMetadataSymbolsWithoutReferences) { + if (options.DisplayAllDefinitions) + { + return true; + } + // If the definition is implicit and we have no references, then we don't want to // clutter the UI with it. if (definition.IsImplicitlyDeclared) diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs index e145e03e49b25..8cc93a4bf90a0 100644 --- a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs +++ b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs @@ -28,6 +28,7 @@ internal static class FeatureAttribute public const string EventHookup = nameof(EventHookup); public const string ExtractMethod = nameof(ExtractMethod); public const string FindReferences = nameof(FindReferences); + public const string SemanticSearch = nameof(SemanticSearch); public const string GlobalOperation = nameof(GlobalOperation); public const string GoToBase = nameof(GoToBase); public const string GoToDefinition = nameof(GoToDefinition); diff --git a/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs b/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs index 046ffcf07b3e1..d56c7ff41d3bb 100644 --- a/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs +++ b/src/Workspaces/Core/Portable/SourceGeneration/IRemoteSourceGenerationService.cs @@ -2,11 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Runtime.Serialization; +using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Text; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.SourceGeneration; @@ -20,7 +21,7 @@ internal interface IRemoteSourceGenerationService /// compare that to the prior generated documents it has to see if it can reuse those directly, or if it needs to /// remove any documents no longer around, add any new documents, or change the contents of any existing documents. /// - ValueTask> GetSourceGenerationInfoAsync( + ValueTask> GetSourceGenerationInfoAsync( Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); /// @@ -30,6 +31,12 @@ internal interface IRemoteSourceGenerationService /// ValueTask> GetContentsAsync( Checksum solutionChecksum, ProjectId projectId, ImmutableArray documentIds, CancellationToken cancellationToken); + + /// + /// Whether or not the specified has source generators or not. + /// + ValueTask HasGeneratorsAsync( + Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken); } /// diff --git a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs index 0d106c168eb18..27422e425657a 100644 --- a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs +++ b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/ISourceGeneratorTelemetryCollectorWorkspaceService.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.SourceGeneratorTelemetry; internal interface ISourceGeneratorTelemetryCollectorWorkspaceService : IWorkspaceService { - void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo, ProjectState project); + void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo, Func getAnalyzerReference); } diff --git a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs index 4414a2e4cfd44..48fa40db7307f 100644 --- a/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs +++ b/src/Workspaces/Core/Portable/SourceGeneratorTelemetry/SourceGeneratorTelemetryCollectorWorkspaceService.cs @@ -42,19 +42,22 @@ public GeneratorTelemetryKey(ISourceGenerator generator, AnalyzerReference analy private readonly StatisticLogAggregator _elapsedTimeByGenerator = new(); private readonly StatisticLogAggregator _producedFilesByGenerator = new(); - private GeneratorTelemetryKey GetTelemetryKey(ISourceGenerator generator, ProjectState project) - => _generatorTelemetryKeys.GetValue(generator, g => new GeneratorTelemetryKey(g, project.GetAnalyzerReferenceForGenerator(g))); + private GeneratorTelemetryKey GetTelemetryKey(ISourceGenerator generator, Func getAnalyzerReference) + => _generatorTelemetryKeys.GetValue(generator, g => new GeneratorTelemetryKey(g, getAnalyzerReference(g))); - public void CollectRunResult(GeneratorDriverRunResult driverRunResult, GeneratorDriverTimingInfo driverTimingInfo, ProjectState project) + public void CollectRunResult( + GeneratorDriverRunResult driverRunResult, + GeneratorDriverTimingInfo driverTimingInfo, + Func getAnalyzerReference) { foreach (var generatorTime in driverTimingInfo.GeneratorTimes) { - _elapsedTimeByGenerator.AddDataPoint(GetTelemetryKey(generatorTime.Generator, project), generatorTime.ElapsedTime); + _elapsedTimeByGenerator.AddDataPoint(GetTelemetryKey(generatorTime.Generator, getAnalyzerReference), generatorTime.ElapsedTime); } foreach (var generatorResult in driverRunResult.Results) { - _producedFilesByGenerator.AddDataPoint(GetTelemetryKey(generatorResult.Generator, project), generatorResult.GeneratedSources.Length); + _producedFilesByGenerator.AddDataPoint(GetTelemetryKey(generatorResult.Generator, getAnalyzerReference), generatorResult.GeneratedSources.Length); } } diff --git a/src/Workspaces/Core/Portable/Tags/WellKnownTags.cs b/src/Workspaces/Core/Portable/Tags/WellKnownTags.cs index 13b86fe85581e..4552c97465f7d 100644 --- a/src/Workspaces/Core/Portable/Tags/WellKnownTags.cs +++ b/src/Workspaces/Core/Portable/Tags/WellKnownTags.cs @@ -50,6 +50,8 @@ public static class WellKnownTags public const string Error = nameof(Error); public const string Warning = nameof(Warning); + internal const string Deprecated = nameof(Deprecated); + internal const string StatusInformation = nameof(StatusInformation); internal const string AddReference = nameof(AddReference); diff --git a/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs b/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs index 28692e25cb05f..451e15a091a56 100644 --- a/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs +++ b/src/Workspaces/Core/Portable/TemporaryStorage/TemporaryStorageServiceFactory.cs @@ -26,7 +26,7 @@ namespace Microsoft.CodeAnalysis.Host; #if NETCOREAPP [SupportedOSPlatform("windows")] #endif -internal partial class TemporaryStorageService : ITemporaryStorageService2 +internal sealed partial class TemporaryStorageService : ITemporaryStorageServiceInternal { /// /// The maximum size in bytes of a single storage unit in a memory mapped file which is shared with other @@ -105,8 +105,9 @@ private TemporaryStorageService(IWorkspaceThreadingService? workspaceThreadingSe public ITemporaryTextStorageInternal CreateTemporaryTextStorage() => new TemporaryTextStorage(this); - public ITemporaryTextStorageInternal AttachTemporaryTextStorage(string storageName, long offset, long size, SourceHashAlgorithm checksumAlgorithm, Encoding? encoding) - => new TemporaryTextStorage(this, storageName, offset, size, checksumAlgorithm, encoding); + public TemporaryTextStorage AttachTemporaryTextStorage( + string storageName, long offset, long size, SourceHashAlgorithm checksumAlgorithm, Encoding? encoding, ImmutableArray contentHash) + => new(this, storageName, offset, size, checksumAlgorithm, encoding, contentHash); ITemporaryStreamStorageInternal ITemporaryStorageServiceInternal.CreateTemporaryStreamStorage() => CreateTemporaryStreamStorage(); @@ -114,8 +115,8 @@ ITemporaryStreamStorageInternal ITemporaryStorageServiceInternal.CreateTemporary internal TemporaryStreamStorage CreateTemporaryStreamStorage() => new(this); - public ITemporaryStreamStorageInternal AttachTemporaryStreamStorage(string storageName, long offset, long size) - => new TemporaryStreamStorage(this, storageName, offset, size); + public TemporaryStreamStorage AttachTemporaryStreamStorage(string storageName, long offset, long size) + => new(this, storageName, offset, size); /// /// Allocate shared storage of a specified size. @@ -167,42 +168,56 @@ private MemoryMappedInfo CreateTemporaryStorage(long size) public static string CreateUniqueName(long size) => "Roslyn Temp Storage " + size.ToString() + " " + Guid.NewGuid().ToString("N"); - private sealed class TemporaryTextStorage : ITemporaryTextStorageInternal, ITemporaryTextStorageWithName + public sealed class TemporaryTextStorage : ITemporaryTextStorageInternal, ITemporaryStorageWithName { private readonly TemporaryStorageService _service; private SourceHashAlgorithm _checksumAlgorithm; private Encoding? _encoding; - private ImmutableArray _checksum; + private ImmutableArray _contentHash; private MemoryMappedInfo? _memoryMappedInfo; public TemporaryTextStorage(TemporaryStorageService service) => _service = service; - public TemporaryTextStorage(TemporaryStorageService service, string storageName, long offset, long size, SourceHashAlgorithm checksumAlgorithm, Encoding? encoding) + public TemporaryTextStorage( + TemporaryStorageService service, + string storageName, + long offset, + long size, + SourceHashAlgorithm checksumAlgorithm, + Encoding? encoding, + ImmutableArray contentHash) { _service = service; _checksumAlgorithm = checksumAlgorithm; _encoding = encoding; + _contentHash = contentHash; _memoryMappedInfo = new MemoryMappedInfo(storageName, offset, size); } // TODO: cleanup https://github.com/dotnet/roslyn/issues/43037 - // Offet, Size not accessed if Name is null + // Offset, Size not accessed if Name is null public string? Name => _memoryMappedInfo?.Name; public long Offset => _memoryMappedInfo!.Offset; public long Size => _memoryMappedInfo!.Size; + + /// + /// Gets the value for the property for the + /// represented by this temporary storage. + /// public SourceHashAlgorithm ChecksumAlgorithm => _checksumAlgorithm; - public Encoding? Encoding => _encoding; - public ImmutableArray GetContentHash() - { - if (_checksum.IsDefault) - { - ImmutableInterlocked.InterlockedInitialize(ref _checksum, ReadText(CancellationToken.None).GetContentHash()); - } + /// + /// Gets the value for the property for the + /// represented by this temporary storage. + /// + public Encoding? Encoding => _encoding; - return _checksum; - } + /// + /// Gets the checksum for the represented by this temporary storage. This is equivalent + /// to calling . + /// + public ImmutableArray ContentHash => _contentHash; public void Dispose() { @@ -213,6 +228,7 @@ public void Dispose() _memoryMappedInfo = null; _encoding = null; + _contentHash = default; } public SourceText ReadText(CancellationToken cancellationToken) @@ -264,6 +280,7 @@ public void WriteText(SourceText text, CancellationToken cancellationToken) { _checksumAlgorithm = text.ChecksumAlgorithm; _encoding = text.Encoding; + _contentHash = text.GetContentHash(); // the method we use to get text out of SourceText uses Unicode (2bytes per char). var size = Encoding.Unicode.GetMaxByteCount(text.Length); @@ -301,7 +318,7 @@ private static unsafe TextReader CreateTextReaderFromTemporaryStorage(UnmanagedM } } - internal class TemporaryStreamStorage : ITemporaryStreamStorageInternal, ITemporaryStorageWithName + internal sealed class TemporaryStreamStorage : ITemporaryStreamStorageInternal, ITemporaryStorageWithName { private readonly TemporaryStorageService _service; private MemoryMappedInfo? _memoryMappedInfo; diff --git a/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs b/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs index e039e9d29ac39..b78767a598ce6 100644 --- a/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs +++ b/src/Workspaces/Core/Portable/Utilities/SegmentedListPool.cs @@ -39,7 +39,7 @@ internal static PooledObject> GetPooledList(out SegmentedLis /// are added to the list, then the singleton will be returned. Otherwise the /// instance will be returned. /// - internal static IList ComputeList( + public static IList ComputeList( Action> addItems, TArgs args, // Only used to allow type inference to work at callsite @@ -60,3 +60,13 @@ internal static IList ComputeList( return list; } } + +internal static class SegmentedListPool +{ + public static IList ComputeList( + Action> addItems, + TArgs args) + { + return SegmentedListPool.ComputeList(addItems, args, _: default); + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Status/WorkspaceStatusService.cs b/src/Workspaces/Core/Portable/Workspace/Host/Status/DefaultWorkspaceStatusService.cs similarity index 80% rename from src/Workspaces/Core/Portable/Workspace/Host/Status/WorkspaceStatusService.cs rename to src/Workspaces/Core/Portable/Workspace/Host/Status/DefaultWorkspaceStatusService.cs index bb3d225a923ba..02c72cb3e4183 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Status/WorkspaceStatusService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Status/DefaultWorkspaceStatusService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Composition; using System.Threading; @@ -14,14 +12,10 @@ namespace Microsoft.CodeAnalysis.Host; [ExportWorkspaceService(typeof(IWorkspaceStatusService), ServiceLayer.Default), Shared] -internal sealed class WorkspaceStatusService : IWorkspaceStatusService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class DefaultWorkspaceStatusService() : IWorkspaceStatusService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public WorkspaceStatusService() - { - } - event EventHandler IWorkspaceStatusService.StatusChanged { add { } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorageService2.cs b/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorageService2.cs deleted file mode 100644 index 9edf4dc0fe7e1..0000000000000 --- a/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorageService2.cs +++ /dev/null @@ -1,25 +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.Text; -using System.Threading; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Host; - -/// -/// This service allows you to access temporary storage. -/// -internal interface ITemporaryStorageService2 : ITemporaryStorageServiceInternal -{ - /// - /// Attach to existing with given name. - /// - ITemporaryStreamStorageInternal AttachTemporaryStreamStorage(string storageName, long offset, long size); - - /// - /// Attach to existing with given name. - /// - ITemporaryTextStorageInternal AttachTemporaryTextStorage(string storageName, long offset, long size, SourceHashAlgorithm checksumAlgorithm, Encoding? encoding); -} diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryTextStorageWithName.cs b/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryTextStorageWithName.cs deleted file mode 100644 index 918fbdd854b0a..0000000000000 --- a/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryTextStorageWithName.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Immutable; -using System.Text; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Host; - -/// -/// Represents a which is used to hold data for . -/// -internal interface ITemporaryTextStorageWithName : ITemporaryTextStorageInternal, ITemporaryStorageWithName -{ - /// - /// Gets the value for the property for the - /// represented by this temporary storage. - /// - SourceHashAlgorithm ChecksumAlgorithm { get; } - - /// - /// Gets the value for the property for the - /// represented by this temporary storage. - /// - Encoding? Encoding { get; } - - /// - /// Gets the checksum for the represented by this temporary storage. This is equivalent - /// to calling . - /// - ImmutableArray GetContentHash(); -} diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IProjectSystemDiagnosticSource.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IProjectSystemDiagnosticSource.cs index e44aebed5200d..272c88bc422f7 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IProjectSystemDiagnosticSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IProjectSystemDiagnosticSource.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.Workspaces.ProjectSystem; @@ -11,9 +9,5 @@ namespace Microsoft.CodeAnalysis.Workspaces.ProjectSystem; // TODO: see if we can get rid of this interface by appropriately rewriting HostDiagnosticUpdateSource to live at the workspaces layer. internal interface IProjectSystemDiagnosticSource { - void ClearAllDiagnosticsForProject(ProjectId projectId); - void ClearAnalyzerReferenceDiagnostics(AnalyzerFileReference fileReference, string language, ProjectId projectId); - void ClearDiagnosticsForProject(ProjectId projectId, object key); DiagnosticData CreateAnalyzerLoadFailureDiagnostic(AnalyzerLoadFailureEventArgs e, string fullPath, ProjectId projectId, string language); - void UpdateDiagnosticsForProject(ProjectId projectId, object key, IEnumerable items); } diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs index 4d6a793c21a02..53e50dee4cf74 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectHostInfo.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/VisualStudioAnalyzer.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/VisualStudioAnalyzer.cs index 2d6003d58fa7a..93437ffffa929 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/VisualStudioAnalyzer.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/VisualStudioAnalyzer.cs @@ -51,7 +51,6 @@ private void OnAnalyzerLoadError(object? sender, AnalyzerLoadFailureEventArgs e) lock (_gate) { _analyzerLoadErrors = _analyzerLoadErrors.Add(data); - projectSystemDiagnosticSource.UpdateDiagnosticsForProject(projectId, this, _analyzerLoadErrors); } } @@ -62,13 +61,6 @@ public void Dispose() if (reference is AnalyzerFileReference fileReference) { fileReference.AnalyzerLoadFailed -= OnAnalyzerLoadError; - - if (!loadErrors.IsEmpty) - { - projectSystemDiagnosticSource.ClearDiagnosticsForProject(projectId, this); - } - - projectSystemDiagnosticSource.ClearAnalyzerReferenceDiagnostics(fileReference, language, projectId); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index d269819d14acf..95519835568d7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -74,6 +74,9 @@ public static Checksum Create(Checksum checksum1, Checksum checksum2) public static Checksum Create(Checksum checksum1, Checksum checksum2, Checksum checksum3) => Create(stackalloc[] { checksum1, checksum2, checksum3 }); + public static Checksum Create(Checksum checksum1, Checksum checksum2, Checksum checksum3, Checksum checksum4) + => Create(stackalloc[] { checksum1, checksum2, checksum3, checksum4 }); + public static Checksum Create(ReadOnlySpan hashes) { Span destination = stackalloc byte[XXHash128SizeBytes]; @@ -82,71 +85,36 @@ public static Checksum Create(ReadOnlySpan hashes) } public static Checksum Create(ArrayBuilder checksums) - { - using var stream = SerializableBytes.CreateWritableStream(); - - using (var writer = new ObjectWriter(stream, leaveOpen: true)) + => Create(checksums, static (checksums, writer) => { foreach (var checksum in checksums) checksum.WriteTo(writer); - } - - stream.Position = 0; - return Create(stream); - } + }); public static Checksum Create(ImmutableArray checksums) - { - using var stream = SerializableBytes.CreateWritableStream(); - - using (var writer = new ObjectWriter(stream, leaveOpen: true)) + => Create(checksums, static (checksums, writer) => { foreach (var checksum in checksums) checksum.WriteTo(writer); - } - - stream.Position = 0; - return Create(stream); - } + }); public static Checksum Create(ImmutableArray bytes) - { - using var stream = SerializableBytes.CreateWritableStream(); - - using (var writer = new ObjectWriter(stream, leaveOpen: true)) + => Create(bytes, static (bytes, writer) => { - for (var i = 0; i < bytes.Length; i++) - writer.WriteByte(bytes[i]); - } - - stream.Position = 0; - return Create(stream); - } + foreach (var b in bytes) + writer.WriteByte(b); + }); public static Checksum Create(T value, ISerializerService serializer) { - using var stream = SerializableBytes.CreateWritableStream(); using var context = new SolutionReplicationContext(); - using (var objectWriter = new ObjectWriter(stream, leaveOpen: true)) - { - serializer.Serialize(value!, objectWriter, context, CancellationToken.None); - } - - stream.Position = 0; - return Create(stream); - } - - public static Checksum Create(ParseOptions value, ISerializerService serializer) - { - using var stream = SerializableBytes.CreateWritableStream(); - - using (var objectWriter = new ObjectWriter(stream, leaveOpen: true)) - { - serializer.SerializeParseOptions(value, objectWriter); - } - - stream.Position = 0; - return Create(stream); + return Create( + (value, serializer, context), + static (tuple, writer) => + { + var (value, serializer, context) = tuple; + serializer.Serialize(value!, writer, context, CancellationToken.None); + }); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs b/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs deleted file mode 100644 index 8bee3099b713a..0000000000000 --- a/src/Workspaces/Core/Portable/Workspace/Solution/FilePathToDocumentIdsMap.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis; - -/// -/// Helper type that keeps track of all the file paths for all the documents in a solution snapshot and all the document -/// ids each maps to. -/// -internal readonly struct FilePathToDocumentIdsMap -{ - private static readonly ImmutableDictionary> s_emptyMap - = ImmutableDictionary.Create>(StringComparer.OrdinalIgnoreCase); - public static readonly FilePathToDocumentIdsMap Empty = new(isFrozen: false, s_emptyMap); - - /// - /// Whether or not this map corresponds to a frozen solution. Frozen solutions commonly drop many documents - /// (because only documents whose trees have been parsed are kept out). To keep things fast, instead of actually - /// dropping all those files from our we instead only keep track of added documents and mark that - /// we're frozen. Then, when a client actually asks for the document ids in a particular solution, we only return the - /// actual set present in that solution instance. - /// - public readonly bool IsFrozen; - private readonly ImmutableDictionary> _map; - - private FilePathToDocumentIdsMap(bool isFrozen, ImmutableDictionary> map) - { - IsFrozen = isFrozen; - _map = map; - } - - public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _map.TryGetValue(filePath, out documentIdsWithPath); - - public static bool operator ==(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => left.IsFrozen == right.IsFrozen && left._map == right._map; - - public static bool operator !=(FilePathToDocumentIdsMap left, FilePathToDocumentIdsMap right) - => !(left == right); - - public override int GetHashCode() - => throw new NotSupportedException(); - - public override bool Equals([NotNullWhen(true)] object? obj) - => obj is FilePathToDocumentIdsMap map && Equals(map); - - public Builder ToBuilder() - => new(IsFrozen, _map.ToBuilder()); - - public FilePathToDocumentIdsMap ToFrozen() - => IsFrozen ? this : new(isFrozen: true, _map); - - public readonly struct Builder - { - private readonly bool _isFrozen; - private readonly ImmutableDictionary>.Builder _builder; - - public Builder(bool isFrozen, ImmutableDictionary>.Builder builder) - { - _isFrozen = isFrozen; - _builder = builder; - } - - public FilePathToDocumentIdsMap ToImmutable() - => new(_isFrozen, _builder.ToImmutable()); - - public bool TryGetValue(string filePath, out ImmutableArray documentIdsWithPath) - => _builder.TryGetValue(filePath, out documentIdsWithPath); - - public void Add(string? filePath, DocumentId documentId) - { - if (RoslynString.IsNullOrEmpty(filePath)) - return; - - _builder.MultiAdd(filePath, documentId); - } - - public void Remove(string? filePath, DocumentId documentId) - { - if (RoslynString.IsNullOrEmpty(filePath)) - return; - - if (!this.TryGetValue(filePath, out var documentIdsWithPath) || !documentIdsWithPath.Contains(documentId)) - throw new ArgumentException($"The given documentId was not found"); - - _builder.MultiRemove(filePath, documentId); - } - } -} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs index 645843fce454e..481b40cf1924b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; @@ -515,7 +514,6 @@ public ProjectAttributes With( VersionStamp? version = null, string? name = null, string? assemblyName = null, - string? language = null, Optional filePath = default, Optional outputPath = default, Optional outputRefPath = default, @@ -530,7 +528,6 @@ public ProjectAttributes With( var newVersion = version ?? Version; var newName = name ?? Name; var newAssemblyName = assemblyName ?? AssemblyName; - var newLanguage = language ?? Language; var newFilePath = filePath.HasValue ? filePath.Value : FilePath; var newOutputPath = outputPath.HasValue ? outputPath.Value : OutputFilePath; var newOutputRefPath = outputRefPath.HasValue ? outputRefPath.Value : OutputRefFilePath; @@ -545,7 +542,6 @@ public ProjectAttributes With( if (newVersion == Version && newName == Name && newAssemblyName == AssemblyName && - newLanguage == Language && newFilePath == FilePath && newOutputPath == OutputFilePath && newOutputRefPath == OutputRefFilePath && @@ -565,7 +561,7 @@ public ProjectAttributes With( newVersion, newName, newAssemblyName, - newLanguage, + Language, newCompilationOutputPaths, newChecksumAlgorithm, defaultNamespace: newDefaultNamespace, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 6577677f41562..013c0f9aaa830 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -14,11 +15,11 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -60,11 +61,6 @@ internal partial class ProjectState private AnalyzerOptions? _lazyAnalyzerOptions; - /// - /// The list of source generators and the analyzer reference they came from. - /// - private ImmutableDictionary? _lazySourceGenerators; - private ProjectState( ProjectInfo projectInfo, LanguageServices languageServices, @@ -736,38 +732,6 @@ public ProjectState WithAnalyzerReferences(IEnumerable analyz return With(projectInfo: ProjectInfo.WithAnalyzerReferences(analyzerReferences).WithVersion(Version.GetNewerVersion())); } - [MemberNotNull(nameof(_lazySourceGenerators))] - private void EnsureSourceGeneratorsInitialized() - { - if (_lazySourceGenerators == null) - { - var builder = ImmutableDictionary.CreateBuilder(); - - foreach (var analyzerReference in AnalyzerReferences) - { - foreach (var generator in analyzerReference.GetGenerators(Language)) - builder.Add(generator, analyzerReference); - } - - Interlocked.CompareExchange(ref _lazySourceGenerators, builder.ToImmutable(), comparand: null); - } - } - - public IEnumerable SourceGenerators - { - get - { - EnsureSourceGeneratorsInitialized(); - return _lazySourceGenerators.Keys; - } - } - - public AnalyzerReference GetAnalyzerReferenceForGenerator(ISourceGenerator generator) - { - EnsureSourceGeneratorsInitialized(); - return _lazySourceGenerators[generator]; - } - public ProjectState AddDocuments(ImmutableArray documents) { if (documents.IsEmpty) @@ -976,4 +940,18 @@ private void GetLatestDependentVersions( ? CreateLazyLatestDocumentTopLevelChangeVersion(newDocument, newDocumentStates, newAdditionalDocumentStates) : _lazyLatestDocumentTopLevelChangeVersion; } + + public void AddDocumentIdsWithFilePath(ref TemporaryArray temporaryArray, string filePath) + { + this.DocumentStates.AddDocumentIdsWithFilePath(ref temporaryArray, filePath); + this.AdditionalDocumentStates.AddDocumentIdsWithFilePath(ref temporaryArray, filePath); + this.AnalyzerConfigDocumentStates.AddDocumentIdsWithFilePath(ref temporaryArray, filePath); + } + + public DocumentId? GetFirstDocumentIdWithFilePath(string filePath) + { + return this.DocumentStates.GetFirstDocumentIdWithFilePath(filePath) ?? + this.AdditionalDocumentStates.GetFirstDocumentIdWithFilePath(filePath) ?? + this.AnalyzerConfigDocumentStates.GetFirstDocumentIdWithFilePath(filePath); + } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index cafe4e3d878c3..ac73cfebc6f1b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1536,9 +1536,17 @@ internal async Task WithMergedLinkedFileChangesAsync( } internal ImmutableArray GetRelatedDocumentIds(DocumentId documentId) - { - return this.SolutionState.GetRelatedDocumentIds(documentId); - } + => this.SolutionState.GetRelatedDocumentIds(documentId); + + /// + /// Returns one of any of the related documents of . Importantly, this will never + /// return (unlike which includes the original + /// file in the result). + /// + /// A hint on the first project to search when looking for related + /// documents. Must not be the project that is from. + internal DocumentId? GetFirstRelatedDocumentId(DocumentId documentId, ProjectId? relatedProjectIdHint) + => this.SolutionState.GetFirstRelatedDocumentId(documentId, relatedProjectIdHint); internal Solution WithNewWorkspace(string? workspaceKind, int workspaceVersion, SolutionServices services) { @@ -1586,9 +1594,10 @@ public Solution WithDocumentText(IEnumerable documentIds, SourceTex /// implementation of where if a user has a source /// generated file open, we need to make sure everything lines up. /// - internal Document WithFrozenSourceGeneratedDocument(SourceGeneratedDocumentIdentity documentIdentity, SourceText text) + internal Document WithFrozenSourceGeneratedDocument( + SourceGeneratedDocumentIdentity documentIdentity, DateTime generationDateTime, SourceText text) { - var newCompilationState = _compilationState.WithFrozenSourceGeneratedDocuments([(documentIdentity, text)]); + var newCompilationState = _compilationState.WithFrozenSourceGeneratedDocuments([(documentIdentity, generationDateTime, text)]); var newSolution = newCompilationState != _compilationState ? new Solution(newCompilationState) : this; @@ -1600,7 +1609,8 @@ internal Document WithFrozenSourceGeneratedDocument(SourceGeneratedDocumentIdent return newProject.GetOrCreateSourceGeneratedDocument(newDocumentState); } - internal Solution WithFrozenSourceGeneratedDocuments(ImmutableArray<(SourceGeneratedDocumentIdentity documentIdentity, SourceText text)> documents) + internal Solution WithFrozenSourceGeneratedDocuments( + ImmutableArray<(SourceGeneratedDocumentIdentity documentIdentity, DateTime generationDateTime, SourceText text)> documents) { var newCompilationState = _compilationState.WithFrozenSourceGeneratedDocuments(documents); return newCompilationState != _compilationState diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs index bf93c6484c821..abffc060a6830 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.CompilationTrackerState.cs @@ -35,8 +35,11 @@ private abstract class CompilationTrackerState /// whether we even have references -- it's pretty likely that running a generator might produce worse results /// than what we originally had. /// + /// This also controls if we will generate skeleton references for cross-language P2P references when + /// creating the compilation for a particular project. When entirely frozen, we do not want to do this due + /// to the enormous cost of emitting ref assemblies for cross language cases. /// - public readonly bool IsFrozen; + public readonly CreationPolicy CreationPolicy; /// /// The best compilation that is available that source generators have not ran on. May be an @@ -47,10 +50,10 @@ private abstract class CompilationTrackerState public CompilationTrackerGeneratorInfo GeneratorInfo { get; } protected CompilationTrackerState( - bool isFrozen, + CreationPolicy creationPolicy, CompilationTrackerGeneratorInfo generatorInfo) { - IsFrozen = isFrozen; + CreationPolicy = creationPolicy; GeneratorInfo = generatorInfo; } } @@ -80,12 +83,12 @@ private sealed class InProgressState : CompilationTrackerState public override Compilation CompilationWithoutGeneratedDocuments => LazyCompilationWithoutGeneratedDocuments.Value; public InProgressState( - bool isFrozen, + CreationPolicy creationPolicy, Lazy compilationWithoutGeneratedDocuments, CompilationTrackerGeneratorInfo generatorInfo, Lazy staleCompilationWithGeneratedDocuments, ImmutableList pendingTranslationActions) - : base(isFrozen, generatorInfo) + : base(creationPolicy, generatorInfo) { // Note: Intermediate projects can be empty. Contract.ThrowIfTrue(pendingTranslationActions is null); @@ -105,13 +108,13 @@ public InProgressState( } public InProgressState( - bool isFrozen, + CreationPolicy creationPolicy, Compilation compilationWithoutGeneratedDocuments, CompilationTrackerGeneratorInfo generatorInfo, Compilation? staleCompilationWithGeneratedDocuments, ImmutableList pendingTranslationActions) : this( - isFrozen, + creationPolicy, new Lazy(() => compilationWithoutGeneratedDocuments), generatorInfo, // Extracted as a method call to prevent captures. @@ -164,20 +167,20 @@ private sealed class FinalCompilationTrackerState : CompilationTrackerState public override Compilation CompilationWithoutGeneratedDocuments { get; } private FinalCompilationTrackerState( - bool isFrozen, + CreationPolicy creationPolicy, Compilation finalCompilationWithGeneratedDocuments, Compilation compilationWithoutGeneratedDocuments, bool hasSuccessfullyLoaded, CompilationTrackerGeneratorInfo generatorInfo, UnrootedSymbolSet unrootedSymbolSet) - : base(isFrozen, generatorInfo) + : base(creationPolicy, generatorInfo) { Contract.ThrowIfNull(finalCompilationWithGeneratedDocuments); // As a policy, all partial-state projects are said to have incomplete references, since the // state has no guarantees. this.CompilationWithoutGeneratedDocuments = compilationWithoutGeneratedDocuments; - HasSuccessfullyLoaded = hasSuccessfullyLoaded && !isFrozen; + HasSuccessfullyLoaded = hasSuccessfullyLoaded; FinalCompilationWithGeneratedDocuments = finalCompilationWithGeneratedDocuments; UnrootedSymbolSet = unrootedSymbolSet; @@ -202,7 +205,7 @@ private FinalCompilationTrackerState( /// Not held onto /// Not held onto public static FinalCompilationTrackerState Create( - bool isFrozen, + CreationPolicy creationPolicy, Compilation finalCompilationWithGeneratedDocuments, Compilation compilationWithoutGeneratedDocuments, bool hasSuccessfullyLoaded, @@ -217,7 +220,7 @@ public static FinalCompilationTrackerState Create( RecordAssemblySymbols(projectId, finalCompilationWithGeneratedDocuments, metadataReferenceToProjectId); return new FinalCompilationTrackerState( - isFrozen, + creationPolicy, finalCompilationWithGeneratedDocuments, compilationWithoutGeneratedDocuments, hasSuccessfullyLoaded, @@ -225,13 +228,15 @@ public static FinalCompilationTrackerState Create( unrootedSymbolSet); } - public FinalCompilationTrackerState WithIsFrozen() - => new(isFrozen: true, - FinalCompilationWithGeneratedDocuments, - CompilationWithoutGeneratedDocuments, - HasSuccessfullyLoaded, - GeneratorInfo, - UnrootedSymbolSet); + public FinalCompilationTrackerState WithCreationPolicy(CreationPolicy creationPolicy) + => creationPolicy == this.CreationPolicy + ? this + : new(creationPolicy, + FinalCompilationWithGeneratedDocuments, + CompilationWithoutGeneratedDocuments, + HasSuccessfullyLoaded, + GeneratorInfo, + UnrootedSymbolSet); private static void RecordAssemblySymbols(ProjectId projectId, Compilation compilation, Dictionary? metadataReferenceToProjectId) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs index 4134f3996f5ce..bdf07adb72933 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker.cs @@ -152,7 +152,7 @@ public ICompilationTracker Fork( }; var newState = new InProgressState( - state.IsFrozen, + state.CreationPolicy, compilationWithoutGeneratedDocuments, state.GeneratorInfo, staleCompilationWithGeneratedDocuments, @@ -334,12 +334,13 @@ InProgressState BuildInProgressStateFromNoCompilationState() var compilationWithoutGeneratedDocuments = CreateEmptyCompilation(); - // We only got here when we had no compilation state at all. So we couldn't have gotten - // here from a frozen state (as a frozen state always ensures we have a - // WithCompilationTrackerState). As such, we can safely still preserve that we're not - // frozen here. + // We only got here when we had no compilation state at all. So we couldn't have gotten here + // from a frozen state (as a frozen state always ensures we have at least an InProgressState). + // As such, we want to start initially in the state where we will both run generators and create + // skeleton references for p2p references. That will ensure the most correct state for our + // compilation the first time we create it. var allSyntaxTreesParsedState = new InProgressState( - isFrozen: false, + CreationPolicy.Create, new Lazy(CreateEmptyCompilation), CompilationTrackerGeneratorInfo.Empty, staleCompilationWithGeneratedDocuments: s_lazyNullCompilation, @@ -358,6 +359,9 @@ async Task CollapseInProgressStateAsync(InProgressState initial { try { + // Only bother keeping track of staleCompilationWithGeneratedDocuments for projects that + // actually have generators in them. + var hasSourceGenerators = await compilationState.HasSourceGeneratorsAsync(this.ProjectState.Id, cancellationToken).ConfigureAwait(false); var currentState = initialState; // Then, we serially process the chain while that parsing is happening concurrently. @@ -367,7 +371,7 @@ async Task CollapseInProgressStateAsync(InProgressState initial // We have a list of transformations to get to our final compilation; take the first transformation and apply it. var (compilationWithoutGeneratedDocuments, staleCompilationWithGeneratedDocuments, generatorInfo) = - await ApplyFirstTransformationAsync(currentState).ConfigureAwait(false); + await ApplyFirstTransformationAsync(currentState, hasSourceGenerators).ConfigureAwait(false); // We have updated state, so store this new result; this allows us to drop the intermediate state we already processed // even if we were to get cancelled at a later point. @@ -380,7 +384,7 @@ async Task CollapseInProgressStateAsync(InProgressState initial // generator docs back to the uncomputed state from that point onwards. We'll just keep // whateverZ generated docs we have. currentState = new InProgressState( - currentState.IsFrozen, + currentState.CreationPolicy, compilationWithoutGeneratedDocuments, generatorInfo, staleCompilationWithGeneratedDocuments, @@ -396,7 +400,7 @@ async Task CollapseInProgressStateAsync(InProgressState initial } async Task<(Compilation compilationWithoutGeneratedDocuments, Compilation? staleCompilationWithGeneratedDocuments, CompilationTrackerGeneratorInfo generatorInfo)> - ApplyFirstTransformationAsync(InProgressState inProgressState) + ApplyFirstTransformationAsync(InProgressState inProgressState, bool hasSourceGenerators) { Contract.ThrowIfTrue(inProgressState.PendingTranslationActions.IsEmpty); var translationAction = inProgressState.PendingTranslationActions[0]; @@ -421,7 +425,7 @@ async Task CollapseInProgressStateAsync(InProgressState initial // Also transform the compilation that has generated files; we won't do that though if the transformation either would cause problems with // the generated documents, or if don't have any source generators in the first place. if (translationAction.CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput && - translationAction.OldProjectState.SourceGenerators.Any()) + hasSourceGenerators) { staleCompilationWithGeneratedDocuments = await translationAction.TransformCompilationAsync(staleCompilationWithGeneratedDocuments, cancellationToken).ConfigureAwait(false); } @@ -472,7 +476,7 @@ async Task FinalizeCompilationWorkerAsync(InProgre Contract.ThrowIfTrue(inProgressState.PendingTranslationActions.Count > 0); // The final state we produce will be frozen or not depending on if a frozen state was passed into it. - var isFrozen = inProgressState.IsFrozen; + var creationPolicy = inProgressState.CreationPolicy; var generatorInfo = inProgressState.GeneratorInfo; var compilationWithoutGeneratedDocuments = inProgressState.CompilationWithoutGeneratedDocuments; var staleCompilationWithGeneratedDocuments = inProgressState.LazyStaleCompilationWithGeneratedDocuments.Value; @@ -524,27 +528,33 @@ await compilationState.GetCompilationAsync( { // Not a submission. Add as a metadata reference. - if (isFrozen) + if (creationPolicy.SkeletonReferenceCreationPolicy is SkeletonReferenceCreationPolicy.Create) { - // In the frozen case, attempt to get a partial reference, or fallback to the last - // successful reference for this project if we can find one. - var metadataReference = compilationState.GetPartialMetadataReference(projectReference, this.ProjectState); + var metadataReference = await compilationState.GetMetadataReferenceAsync(projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); + AddMetadataReference(projectReference, metadataReference); + } + else + { + Contract.ThrowIfFalse(creationPolicy.SkeletonReferenceCreationPolicy is SkeletonReferenceCreationPolicy.CreateIfAbsent or SkeletonReferenceCreationPolicy.DoNotCreate); + // If not asked to explicit create an up to date skeleton, attempt to get a partial + // reference, or fallback to the last successful reference for this project if we can + // find one. + var metadataReference = compilationState.GetPartialMetadataReference(projectReference, this.ProjectState); if (metadataReference is null) { - // if we failed to get the metadata and we were frozen, check to see if we - // previously had existing metadata and reuse it instead. var inProgressCompilationNotRef = staleCompilationWithGeneratedDocuments ?? compilationWithoutGeneratedDocuments; metadataReference = inProgressCompilationNotRef.ExternalReferences.FirstOrDefault( r => GetProjectId(inProgressCompilationNotRef.GetAssemblyOrModuleSymbol(r) as IAssemblySymbol) == projectReference.ProjectId); } - AddMetadataReference(projectReference, metadataReference); - } - else - { - // For the non-frozen case, attempt to get the full metadata reference. - var metadataReference = await compilationState.GetMetadataReferenceAsync(projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); + // If we still failed, but our policy is to create when absent, then do the work to + // create a real skeleton here. + if (metadataReference is null && creationPolicy.SkeletonReferenceCreationPolicy is SkeletonReferenceCreationPolicy.CreateIfAbsent) + { + metadataReference = await compilationState.GetMetadataReferenceAsync(projectReference, this.ProjectState, cancellationToken).ConfigureAwait(false); + } + AddMetadataReference(projectReference, metadataReference); } } @@ -562,19 +572,16 @@ await compilationState.GetCompilationAsync( } // We will finalize the compilation by adding full contents here. - var (compilationWithGeneratedDocuments, generatedDocuments, generatorDriver) = await AddExistingOrComputeNewGeneratorInfoAsync( - isFrozen, + var (compilationWithGeneratedDocuments, nextGeneratorInfo) = await AddExistingOrComputeNewGeneratorInfoAsync( + creationPolicy, compilationState, compilationWithoutGeneratedDocuments, generatorInfo, staleCompilationWithGeneratedDocuments, cancellationToken).ConfigureAwait(false); - // After producing the sg documents, we must always be in the final state for the generator data. - var nextGeneratorInfo = new CompilationTrackerGeneratorInfo(generatedDocuments, generatorDriver); - var finalState = FinalCompilationTrackerState.Create( - isFrozen, + creationPolicy, compilationWithGeneratedDocuments, compilationWithoutGeneratedDocuments, hasSuccessfullyLoaded, @@ -667,13 +674,18 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke { var state = this.ReadState(); + // We're freezing the solution for features where latency performance is parameter. Do not run SGs or + // create skeleton references at this point. Just use whatever we've already generated for each in the + // past. + var desiredCreationPolicy = CreationPolicy.DoNotCreate; + if (state is FinalCompilationTrackerState finalState) { - // If we're finalized and already frozen, we can just use ourselves. Otherwise, flip the frozen bit - // so that any future forks keep things frozen. - return finalState.IsFrozen + // Attempt to transition our state to one where we do not run generators or create skeleton references. + var newFinalState = finalState.WithCreationPolicy(desiredCreationPolicy); + return newFinalState == finalState ? this - : new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), skeletonReferenceCacheToClone: _skeletonReferenceCache); + : new CompilationTracker(this.ProjectState, newFinalState, skeletonReferenceCacheToClone: _skeletonReferenceCache); } // Non-final state currently. Produce an in-progress-state containing the forked change. Note: we @@ -722,7 +734,7 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke return new CompilationTracker( frozenProjectState, new InProgressState( - isFrozen: true, + desiredCreationPolicy, lazyCompilationWithoutGeneratedDocuments, CompilationTrackerGeneratorInfo.Empty, lazyCompilationWithGeneratedDocuments, @@ -748,7 +760,7 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke return new CompilationTracker( frozenProjectState, new InProgressState( - isFrozen: true, + desiredCreationPolicy, compilationWithoutGeneratedDocuments, generatorInfo, compilationWithGeneratedDocuments, @@ -765,10 +777,8 @@ public async ValueTask> GetSour SolutionCompilationState compilationState, CancellationToken cancellationToken) { // If we don't have any generators, then we know we have no generated files, so we can skip the computation entirely. - if (!this.ProjectState.SourceGenerators.Any()) - { + if (!await compilationState.HasSourceGeneratorsAsync(this.ProjectState.Id, cancellationToken).ConfigureAwait(false)) return TextDocumentStates.Empty; - } var finalState = await GetOrBuildFinalStateAsync( compilationState, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -778,10 +788,8 @@ public async ValueTask> GetSour public async ValueTask> GetSourceGeneratorDiagnosticsAsync( SolutionCompilationState compilationState, CancellationToken cancellationToken) { - if (!this.ProjectState.SourceGenerators.Any()) - { + if (!await compilationState.HasSourceGeneratorsAsync(this.ProjectState.Id, cancellationToken).ConfigureAwait(false)) return []; - } var finalState = await GetOrBuildFinalStateAsync( compilationState, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -807,10 +815,8 @@ public async ValueTask> GetSourceGeneratorDiagnostics public async ValueTask GetSourceGeneratorRunResultAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken) { - if (!this.ProjectState.SourceGenerators.Any()) - { + if (!await compilationState.HasSourceGeneratorsAsync(this.ProjectState.Id, cancellationToken).ConfigureAwait(false)) return null; - } var finalState = await GetOrBuildFinalStateAsync( compilationState, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs index 73fbee3c5a223..fc30739ae9675 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CompilationTracker_Generators.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; using System.Linq; @@ -26,15 +27,15 @@ internal partial class SolutionCompilationState { private partial class CompilationTracker : ICompilationTracker { - private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments, GeneratorDriver? generatorDriver)> AddExistingOrComputeNewGeneratorInfoAsync( - bool isFrozen, + private async Task<(Compilation compilationWithGeneratedFiles, CompilationTrackerGeneratorInfo nextGeneratorInfo)> AddExistingOrComputeNewGeneratorInfoAsync( + CreationPolicy creationPolicy, SolutionCompilationState compilationState, Compilation compilationWithoutGeneratedFiles, CompilationTrackerGeneratorInfo generatorInfo, Compilation? compilationWithStaleGeneratedTrees, CancellationToken cancellationToken) { - if (isFrozen) + if (creationPolicy.GeneratedDocumentCreationPolicy is GeneratedDocumentCreationPolicy.DoNotCreate) { // We're frozen. So we do not want to go through the expensive cost of running generators. Instead, we // just whatever prior generated docs we have. @@ -42,54 +43,34 @@ private partial class CompilationTracker : ICompilationTracker static (state, cancellationToken) => state.GetSyntaxTreeAsync(cancellationToken), cancellationToken).ConfigureAwait(false); var compilationWithGeneratedFiles = compilationWithoutGeneratedFiles.AddSyntaxTrees(generatedSyntaxTrees); - return (compilationWithGeneratedFiles, generatorInfo.Documents, generatorInfo.Driver); - } - if (!this.ProjectState.SourceGenerators.Any()) - { - // We don't have any source generators. Trivially bail out. - var compilationWithGeneratedFiles = compilationWithoutGeneratedFiles; - return (compilationWithGeneratedFiles, TextDocumentStates.Empty, generatorInfo.Driver); + // Return the old generator info as is. + return (compilationWithGeneratedFiles, generatorInfo); } - - return await ComputeNewGeneratorInfoAsync( - compilationState, - compilationWithoutGeneratedFiles, - generatorInfo.Documents, - generatorInfo.Driver, - compilationWithStaleGeneratedTrees, - cancellationToken).ConfigureAwait(false); - } - - private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments, GeneratorDriver? generatorDriver)> ComputeNewGeneratorInfoAsync( - SolutionCompilationState compilationState, - Compilation compilationWithoutGeneratedFiles, - TextDocumentStates oldGeneratedDocuments, - GeneratorDriver? oldGeneratorDriver, - Compilation? compilationWithStaleGeneratedTrees, - CancellationToken cancellationToken) - { - // First try to compute the SG docs in the remote process (if we're the host process), syncing the results - // back over to us to ensure that both processes are in total agreement about the SG docs and their - // contents. - var result = await TryComputeNewGeneratorInfoInRemoteProcessAsync( - compilationState, compilationWithoutGeneratedFiles, oldGeneratedDocuments, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); - if (result.HasValue) + else { - // Since we ran the SG work out of process, we could not have created or modified the driver passed in. - // So just pass what we got in right back out. - return (result.Value.compilationWithGeneratedFiles, result.Value.generatedDocuments, oldGeneratorDriver); - } + // First try to compute the SG docs in the remote process (if we're the host process), syncing the results + // back over to us to ensure that both processes are in total agreement about the SG docs and their + // contents. + var result = await TryComputeNewGeneratorInfoInRemoteProcessAsync( + compilationState, compilationWithoutGeneratedFiles, generatorInfo.Documents, compilationWithStaleGeneratedTrees, cancellationToken).ConfigureAwait(false); + if (result.HasValue) + { + // Since we ran the SG work out of process, we could not have created or modified the driver passed in. + // Just return `null` for the driver as there's nothing to track for it on the host side. + return (result.Value.compilationWithGeneratedFiles, new(result.Value.generatedDocuments, Driver: null)); + } - // If that failed (OOP crash, or we are the OOP process ourselves), then generate the SG docs locally. - var telemetryCollector = compilationState.SolutionState.Services.GetService(); - return await ComputeNewGeneratorInfoInCurrentProcessAsync( - telemetryCollector, - compilationWithoutGeneratedFiles, - oldGeneratedDocuments, - oldGeneratorDriver, - compilationWithStaleGeneratedTrees, - cancellationToken).ConfigureAwait(false); + // If that failed (OOP crash, or we are the OOP process ourselves), then generate the SG docs locally. + var (compilationWithGeneratedFiles, nextGeneratedDocuments, nextGeneratorDriver) = await ComputeNewGeneratorInfoInCurrentProcessAsync( + compilationState, + compilationWithoutGeneratedFiles, + generatorInfo.Documents, + generatorInfo.Driver, + compilationWithStaleGeneratedTrees, + cancellationToken).ConfigureAwait(false); + return (compilationWithGeneratedFiles, new(nextGeneratedDocuments, nextGeneratorDriver)); + } } private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments)?> TryComputeNewGeneratorInfoInRemoteProcessAsync( @@ -123,13 +104,18 @@ private partial class CompilationTracker : ICompilationTracker if (!infosOpt.HasValue) return null; + var infos = infosOpt.Value; + + // If there are no generated documents, bail out immediately. + if (infos.Length == 0) + return (compilationWithoutGeneratedFiles, TextDocumentStates.Empty); + // Next, figure out what is different locally. Specifically, what documents we don't know about, or we // know about but whose text contents are different. using var _1 = ArrayBuilder.GetInstance(out var documentsToAddOrUpdate); using var _2 = PooledDictionary.GetInstance(out var documentIdToIndex); - var infos = infosOpt.Value; - foreach (var (documentIdentity, contentIdentity) in infos) + foreach (var (documentIdentity, contentIdentity, _) in infos) { var documentId = documentIdentity.DocumentId; Contract.ThrowIfFalse(documentId.IsSourceGenerated); @@ -155,6 +141,13 @@ private partial class CompilationTracker : ICompilationTracker compilationWithStaleGeneratedTrees != null && oldGeneratedDocuments.States.All(kvp => kvp.Value.ParseOptions.Equals(this.ProjectState.ParseOptions))) { + // Even though non of the contents changed, it's possible that the timestamps on them did. + foreach (var (documentIdentity, _, generationDateTime) in infos) + { + var documentId = documentIdentity.DocumentId; + oldGeneratedDocuments = oldGeneratedDocuments.SetState(documentId, oldGeneratedDocuments.GetRequiredState(documentId).WithGenerationDateTime(generationDateTime)); + } + // If there are no generated documents though, then just use the compilationWithoutGeneratedFiles so we // only hold onto that single compilation from this point on. return oldGeneratedDocuments.Count == 0 @@ -180,7 +173,7 @@ private partial class CompilationTracker : ICompilationTracker // Now go through and produce the new document states, using what we have already if it is unchanged, or // what we have retrieved for anything new/changed. using var generatedDocumentsBuilder = TemporaryArray.Empty; - foreach (var (documentIdentity, contentIdentity) in infos) + foreach (var (documentIdentity, contentIdentity, generationDateTime) in infos) { var documentId = documentIdentity.DocumentId; Contract.ThrowIfFalse(documentId.IsSourceGenerated); @@ -200,7 +193,8 @@ private partial class CompilationTracker : ICompilationTracker // Server provided us the checksum, so we just pass that along. Note: it is critical that we do // this as it may not be possible to reconstruct the same checksum the server produced due to // the lossy nature of source texts. See comment on GetOriginalSourceTextChecksum for more detail. - contentIdentity.OriginalSourceTextContentHash); + contentIdentity.OriginalSourceTextContentHash, + generationDateTime); Contract.ThrowIfTrue(generatedDocument.GetOriginalSourceTextContentHash() != contentIdentity.OriginalSourceTextContentHash, "Checksums must match!"); generatedDocumentsBuilder.Add(generatedDocument); } @@ -213,7 +207,9 @@ private partial class CompilationTracker : ICompilationTracker // ParseOptions may have changed between last generation and this one. Ensure that they are // properly propagated to the generated doc. - generatedDocumentsBuilder.Add(existingDocument.WithParseOptions(this.ProjectState.ParseOptions!)); + generatedDocumentsBuilder.Add(existingDocument + .WithParseOptions(this.ProjectState.ParseOptions!) + .WithGenerationDateTime(generationDateTime)); } } @@ -225,13 +221,17 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( } private async Task<(Compilation compilationWithGeneratedFiles, TextDocumentStates generatedDocuments, GeneratorDriver? generatorDriver)> ComputeNewGeneratorInfoInCurrentProcessAsync( - ISourceGeneratorTelemetryCollectorWorkspaceService? telemetryCollector, + SolutionCompilationState compilationState, Compilation compilationWithoutGeneratedFiles, TextDocumentStates oldGeneratedDocuments, GeneratorDriver? generatorDriver, Compilation? compilationWithStaleGeneratedTrees, CancellationToken cancellationToken) { + // If we don't have any source generators. Trivially bail out. + if (!await compilationState.HasSourceGeneratorsAsync(this.ProjectState.Id, cancellationToken).ConfigureAwait(false)) + return (compilationWithoutGeneratedFiles, TextDocumentStates.Empty, generatorDriver); + // If we don't already have an existing generator driver, create one from scratch generatorDriver ??= CreateGeneratorDriver(this.ProjectState); @@ -264,7 +264,10 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( var runResult = generatorDriver.GetRunResult(); - telemetryCollector?.CollectRunResult(runResult, generatorDriver.GetTimingInfo(), ProjectState); + var telemetryCollector = compilationState.SolutionState.Services.GetService(); + telemetryCollector?.CollectRunResult( + runResult, generatorDriver.GetTimingInfo(), + g => GetAnalyzerReference(this.ProjectState, g)); // We may be able to reuse compilationWithStaleGeneratedTrees if the generated trees are identical. We will assign null // to compilationWithStaleGeneratedTrees if we at any point realize it can't be used. We'll first check the count of trees @@ -281,12 +284,15 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( } using var generatedDocumentsBuilder = TemporaryArray.Empty; + + // Capture the date now. We want all the generated files to use this date consistently. + var generationDateTime = DateTime.Now; foreach (var generatorResult in runResult.Results) { if (IsGeneratorRunResultToIgnore(generatorResult)) continue; - var generatorAnalyzerReference = this.ProjectState.GetAnalyzerReferenceForGenerator(generatorResult.Generator); + var generatorAnalyzerReference = GetAnalyzerReference(this.ProjectState, generatorResult.Generator); foreach (var generatedSource in generatorResult.GeneratedSources) { @@ -302,10 +308,15 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( .WithText(generatedSource.SourceText) .WithParseOptions(this.ProjectState.ParseOptions!); - generatedDocumentsBuilder.Add(newDocument); - + // If changing the text/parse-options actually produced something new, then we can't use the + // stale trees. We also want to mark this point at the point when the document was generated. if (newDocument != existing) + { compilationWithStaleGeneratedTrees = null; + newDocument = newDocument.WithGenerationDateTime(generationDateTime); + } + + generatedDocumentsBuilder.Add(newDocument); } else { @@ -325,7 +336,8 @@ await newGeneratedDocuments.States.Values.SelectAsArrayAsync( generatedSource.SyntaxTree.Options, ProjectState.LanguageServices, // Compute the checksum on demand from the given source text. - originalSourceTextChecksum: null)); + originalSourceTextChecksum: null, + generationDateTime)); // The count of trees was the same, but something didn't match up. Since we're here, at least one tree // was added, and an equal number must have been removed. Rather than trying to incrementally update @@ -381,7 +393,7 @@ static GeneratorDriver CreateGeneratorDriver(ProjectState projectState) return compilationFactory.CreateGeneratorDriver( projectState.ParseOptions!, - projectState.SourceGenerators.ToImmutableArray(), + GetSourceGenerators(projectState), projectState.AnalyzerOptions.AnalyzerConfigOptionsProvider, additionalTexts); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CreationPolicy.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CreationPolicy.cs new file mode 100644 index 0000000000000..ff61c5044a6a4 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.CreationPolicy.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis; + +internal partial class SolutionCompilationState +{ + /// + /// Flags controlling if generator documents should be created or not. + /// + private enum GeneratedDocumentCreationPolicy + { + /// + /// Source generators should be run and should produce up to date results. + /// + Create, + + /// + /// Source generators should not run. Whatever results were previously computed should be reused. + /// + DoNotCreate, + } + + /// + /// Flags controlling if skeleton references should be created or not. + /// + private enum SkeletonReferenceCreationPolicy + { + /// + /// Skeleton references should be created, and should be up to date with the project they are created for. + /// + Create, + + /// + /// Skeleton references should only be created for a compilation if no existing skeleton exists for their + /// project from some point in the past. + /// + CreateIfAbsent, + + /// + /// Skeleton references should not be created at all. + /// + DoNotCreate, + } + + private readonly record struct CreationPolicy( + GeneratedDocumentCreationPolicy GeneratedDocumentCreationPolicy, + SkeletonReferenceCreationPolicy SkeletonReferenceCreationPolicy) + { + /// + /// Create up to date source generator docs and create up to date skeleton references when needed. + /// + public static readonly CreationPolicy Create = new(GeneratedDocumentCreationPolicy.Create, SkeletonReferenceCreationPolicy.Create); + + /// + /// Do not create up to date source generator docs and do not create up to date skeleton references for P2P + /// references. For both, use whatever has been generated most recently. + /// + public static readonly CreationPolicy DoNotCreate = new(GeneratedDocumentCreationPolicy.DoNotCreate, SkeletonReferenceCreationPolicy.DoNotCreate); + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 9f15aed0ffec4..30961e91913ed 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -355,7 +355,7 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver _) .ReplaceAdditionalTexts(this.NewProjectState.AdditionalDocumentStates.SelectAsArray(static documentState => documentState.AdditionalText)) .WithUpdatedParseOptions(this.NewProjectState.ParseOptions!) .WithUpdatedAnalyzerConfigOptions(this.NewProjectState.AnalyzerOptions.AnalyzerConfigOptionsProvider) - .ReplaceGenerators(this.NewProjectState.SourceGenerators.ToImmutableArray()); + .ReplaceGenerators(GetSourceGenerators(this.NewProjectState)); return generatorDriver; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 4f39efd34e3a1..401fccdea847c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -115,7 +115,7 @@ private SolutionCompilationState Branch( if (newSolutionState == this.SolutionState && projectIdToTrackerMap == _projectIdToTrackerMap && - newFrozenSourceGeneratedDocumentStates.Equals(FrozenSourceGeneratedDocumentStates)) + Equals(newFrozenSourceGeneratedDocumentStates, FrozenSourceGeneratedDocumentStates)) { return this; } @@ -1010,7 +1010,7 @@ public SolutionCompilationState WithoutFrozenSourceGeneratedDocuments() if (FrozenSourceGeneratedDocumentStates == null) return this; - var projectIdsToUnfreeze = FrozenSourceGeneratedDocumentStates.Value.States.Values + var projectIdsToUnfreeze = FrozenSourceGeneratedDocumentStates.States.Values .Select(static state => state.Identity.DocumentId.ProjectId) .Distinct() .ToImmutableArray(); @@ -1046,7 +1046,7 @@ public SolutionCompilationState WithoutFrozenSourceGeneratedDocuments() /// generated file open, we need to make sure everything lines up. /// public SolutionCompilationState WithFrozenSourceGeneratedDocuments( - ImmutableArray<(SourceGeneratedDocumentIdentity documentIdentity, SourceText sourceText)> documents) + ImmutableArray<(SourceGeneratedDocumentIdentity documentIdentity, DateTime generationDateTime, SourceText sourceText)> documents) { // We won't support freezing multiple source generated documents more than once in a chain, simply because we have no need // to support that; these solutions are created on demand when we need to operate on an open source generated document, @@ -1060,14 +1060,15 @@ public SolutionCompilationState WithFrozenSourceGeneratedDocuments( // We'll keep track if every document we're reusing is the exact same as the final generated output we already have using var _ = ArrayBuilder.GetInstance(documents.Length, out var documentStates); - foreach (var (documentIdentity, sourceText) in documents) + foreach (var (documentIdentity, generationDateTime, sourceText) in documents) { var existingGeneratedState = TryGetSourceGeneratedDocumentStateForAlreadyGeneratedId(documentIdentity.DocumentId); if (existingGeneratedState != null) { var newGeneratedState = existingGeneratedState .WithText(sourceText) - .WithParseOptions(existingGeneratedState.ParseOptions); + .WithParseOptions(existingGeneratedState.ParseOptions) + .WithGenerationDateTime(generationDateTime); // If the content already matched, we can just reuse the existing state, so we don't need to track this one if (newGeneratedState != existingGeneratedState) @@ -1083,7 +1084,8 @@ public SolutionCompilationState WithFrozenSourceGeneratedDocuments( projectState.ParseOptions!, projectState.LanguageServices, // Just compute the checksum from the source text passed in. - originalSourceTextChecksum: null); + originalSourceTextChecksum: null, + generationDateTime); documentStates.Add(newGeneratedState); } } @@ -1142,19 +1144,6 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell var newIdToProjectStateMapBuilder = this.SolutionState.ProjectStates.ToBuilder(); var newIdToTrackerMapBuilder = _projectIdToTrackerMap.ToBuilder(); - // Keep track of the files that were potentially added between the last frozen snapshot point we have for a - // project and now. Specifically, if a file was removed in reality, it may show us as an add as we are - // effectively jumping back to a prior point in time for a particular project. We want all those files (and - // related doc ids) to be present in the frozen solution we hand back. - // - // Note: we only keep track of added files. We do not keep track of removed files. This is intentionally done - // for performance reasons. Specifically, it is quite normal for a project to drop all documents when frozen - // (for example, when no documents have been parsed in it). Actually dropping all these files from this map is - // very expensive. This does mean that the FilePathToDocumentIdsMap will be a superset of all files. That's - // ok. We'll mark this map as being frozen (and thus potentially containing a superset of legal ids), and later - // on our helpers will check for that and filter down to the set that is in a solution when queried. - var filePathToDocumentIdsMapBuilder = this.SolutionState.FilePathToDocumentIdsMap.ToFrozen().ToBuilder(); - foreach (var projectId in this.SolutionState.ProjectIds) { cancellationToken.ThrowIfCancellationRequested(); @@ -1175,39 +1164,15 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell newIdToProjectStateMapBuilder[projectId] = newProjectState; newIdToTrackerMapBuilder[projectId] = newTracker; - - // Freezing projects can cause them to have an entirely different set of documents (since it effectively - // rewinds the project back to the last time it produced a compilation). Ensure we keep track of the docs - // added or removed from the project states to keep the final filepath-to-documentid map accurate. - // - // Note: we only have to do this if the actual project-state changed. If we were able to use the same - // instance (common if we already got the compilation for a project), then nothing changes with the set - // of documents. - // - // Examples of where the documents may absolutely change though are when we haven't even gotten a - // compilation yet. In that case, the project transitions to an empty state, which means we should remove - // all its documents from the filePathToDocumentIdsMap. Similarly, if we were at some in-progress-state we - // might reset the project back to a prior state from when the last compilation was requested, losing - // information about documents recently added or removed. - - if (oldProjectState != newProjectState) - { - AddMissingOrChangedFilePathMappings(filePathToDocumentIdsMapBuilder, oldProjectState.DocumentStates, newProjectState.DocumentStates); - AddMissingOrChangedFilePathMappings(filePathToDocumentIdsMapBuilder, oldProjectState.AdditionalDocumentStates, newProjectState.AdditionalDocumentStates); - AddMissingOrChangedFilePathMappings(filePathToDocumentIdsMapBuilder, oldProjectState.AnalyzerConfigDocumentStates, newProjectState.AnalyzerConfigDocumentStates); - } } var newIdToProjectStateMap = newIdToProjectStateMapBuilder.ToImmutable(); var newIdToTrackerMap = newIdToTrackerMapBuilder.ToImmutable(); - var filePathToDocumentIdsMap = filePathToDocumentIdsMapBuilder.ToImmutable(); - var dependencyGraph = SolutionState.CreateDependencyGraph(this.SolutionState.ProjectIds, newIdToProjectStateMap); var newState = this.SolutionState.Branch( idToProjectStateMap: newIdToProjectStateMap, - filePathToDocumentIdsMap: filePathToDocumentIdsMap, dependencyGraph: dependencyGraph); var newCompilationState = this.Branch( @@ -1217,41 +1182,6 @@ private SolutionCompilationState ComputeFrozenSnapshot(CancellationToken cancell cachedFrozenSnapshot: _cachedFrozenSnapshot); return newCompilationState; - - static void AddMissingOrChangedFilePathMappings( - FilePathToDocumentIdsMap.Builder filePathToDocumentIdsMapBuilder, - TextDocumentStates oldStates, - TextDocumentStates newStates) where TDocumentState : TextDocumentState - { - if (oldStates.Equals(newStates)) - return; - - // We want to make sure that all the documents in the new-state are properly represented in the file map. - // It's ok if old-state documents are still in the map as GetDocumentIdsWithFilePath will filter them out - // later since we're producing a frozen-partial map. - // - // Iterating over the new-states has an additional benefit. For projects that haven't ever been looked at - // (so they haven't really parsed any documents), this will results in empty new-states. So this loop will - // be almost a no-op for most non-relevant projects. - foreach (var (documentId, newDocumentState) in newStates.States) - { - if (!oldStates.TryGetState(documentId, out var oldDocumentState)) - { - // Keep track of files that are definitely added. Make sure the added doc is in the file path map. - filePathToDocumentIdsMapBuilder.Add(newDocumentState.FilePath, documentId); - - } - else if (oldDocumentState != newDocumentState && - oldDocumentState.FilePath != newDocumentState.FilePath) - { - // Otherwise, if the document is in both, but the file name changed, then remove the old mapping - // and add the new mapping. Importantly, we don't want other linked files with the *old* path - // to consider this document one of their linked brethren. - filePathToDocumentIdsMapBuilder.Remove(oldDocumentState.FilePath, oldDocumentState.Id); - filePathToDocumentIdsMapBuilder.Add(newDocumentState.FilePath, newDocumentState.Id); - } - } - } } /// @@ -1467,9 +1397,7 @@ private SolutionCompilationState AddDocumentsToMultipleProjects( var stateChange = newCompilationState.SolutionState.ForkProject( oldProjectState, - newProjectState, - // intentionally accessing this.Solution here not newSolutionState - newFilePathToDocumentIdsMap: this.SolutionState.CreateFilePathToDocumentIdsMapWithAddedDocuments(newDocumentStates)); + newProjectState); newCompilationState = newCompilationState.ForkProject( stateChange, @@ -1521,9 +1449,7 @@ private SolutionCompilationState RemoveDocumentsFromMultipleProjects( var stateChange = newCompilationState.SolutionState.ForkProject( oldProjectState, - newProjectState, - // Intentionally using this.Solution here and not newSolutionState - newFilePathToDocumentIdsMap: this.SolutionState.CreateFilePathToDocumentIdsMapWithRemovedDocuments(removedDocumentStatesForProject)); + newProjectState); newCompilationState = newCompilationState.ForkProject( stateChange, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs index 830d52cd72c3d..e76fb860f2879 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -123,20 +124,23 @@ public async Task GetChecksumAsync(ProjectId projectId, CancellationTo ChecksumCollection? frozenSourceGeneratedDocumentIdentities = null; ChecksumsAndIds? frozenSourceGeneratedDocuments = null; + ImmutableArray frozenSourceGeneratedDocumentGenerationDateTimes = default; - if (FrozenSourceGeneratedDocumentStates.HasValue) + if (FrozenSourceGeneratedDocumentStates != null) { var serializer = this.SolutionState.Services.GetRequiredService(); - var identityChecksums = FrozenSourceGeneratedDocumentStates.Value + var identityChecksums = FrozenSourceGeneratedDocumentStates .SelectAsArray(static (s, arg) => arg.serializer.CreateChecksum(s.Identity, cancellationToken: arg.cancellationToken), (serializer, cancellationToken)); frozenSourceGeneratedDocumentIdentities = new ChecksumCollection(identityChecksums); - frozenSourceGeneratedDocuments = await FrozenSourceGeneratedDocumentStates.Value.GetChecksumsAndIdsAsync(cancellationToken).ConfigureAwait(false); + frozenSourceGeneratedDocuments = await FrozenSourceGeneratedDocumentStates.GetChecksumsAndIdsAsync(cancellationToken).ConfigureAwait(false); + frozenSourceGeneratedDocumentGenerationDateTimes = FrozenSourceGeneratedDocumentStates.SelectAsArray(d => d.GenerationDateTime); } var compilationStateChecksums = new SolutionCompilationStateChecksums( solutionStateChecksum, frozenSourceGeneratedDocumentIdentities, - frozenSourceGeneratedDocuments); + frozenSourceGeneratedDocuments, + frozenSourceGeneratedDocumentGenerationDateTimes); return (compilationStateChecksums, projectCone); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_SourceGenerators.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_SourceGenerators.cs new file mode 100644 index 0000000000000..915b3adc72a60 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_SourceGenerators.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more 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.Frozen; +using System.Collections.Immutable; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Collections; +using Microsoft.CodeAnalysis.SourceGeneration; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis; + +internal partial class SolutionCompilationState +{ + private sealed record SourceGeneratorMap( + ImmutableArray SourceGenerators, + FrozenDictionary SourceGeneratorToAnalyzerReference); + + /// + /// Cached mapping from language (only C#/VB since those are the only languages that support analyzers) to the lists + /// of analyzer references (see ) to all the s produced by those references. This should only be created and cached on the OOP side + /// of things so that we don't cause source generators to be loaded (and fixed) within VS (which is .net framework + /// only). + /// + private static readonly ConditionalWeakTable s_projectStateToSourceGeneratorsMap = new(); + + /// + /// Cached information about if a project has source generators or not. Note: this is distinct from as we want to be able to compute it by calling over to our OOP + /// process (if present) and having it make the determination, without the host necessarily loading generators + /// itself. + /// + private static readonly ConditionalWeakTable> s_hasSourceGeneratorsMap = new(); + + /// + /// This method should only be called in a .net core host like our out of process server. + /// + private static ImmutableArray GetSourceGenerators(ProjectState projectState) + { + var map = GetSourceGeneratorMap(projectState); + return map is null ? [] : map.SourceGenerators; + } + + /// + /// This method should only be called in a .net core host like our out of process server. + /// + private static AnalyzerReference GetAnalyzerReference(ProjectState projectState, ISourceGenerator sourceGenerator) + { + // We must be talking about a project that supports compilations, since we already got a source generator from it. + Contract.ThrowIfFalse(projectState.SupportsCompilation); + + var map = GetSourceGeneratorMap(projectState); + + // It should not be possible for this to be null. We have the source generator, as such we must have mapped from + // the project state to the SG info for it. + Contract.ThrowIfNull(map); + + return map.SourceGeneratorToAnalyzerReference[sourceGenerator]; + } + + private static SourceGeneratorMap? GetSourceGeneratorMap(ProjectState projectState) + { + if (!projectState.SupportsCompilation) + return null; + + return s_projectStateToSourceGeneratorsMap.GetValue(projectState, ComputeSourceGenerators); + + static SourceGeneratorMap ComputeSourceGenerators(ProjectState projectState) + { + using var generators = TemporaryArray.Empty; + using var _ = PooledDictionary.GetInstance(out var generatorToAnalyzerReference); + + foreach (var reference in projectState.AnalyzerReferences) + { + foreach (var generator in reference.GetGenerators(projectState.Language).Distinct()) + { + generators.Add(generator); + generatorToAnalyzerReference.Add(generator, reference); + } + } + + return new(generators.ToImmutableAndClear(), generatorToAnalyzerReference.ToFrozenDictionary()); + } + } + + public async Task HasSourceGeneratorsAsync(ProjectId projectId, CancellationToken cancellationToken) + { + var projectState = this.SolutionState.GetRequiredProjectState(projectId); + + if (!s_hasSourceGeneratorsMap.TryGetValue(projectState, out var lazy)) + { + // Extracted into local function to prevent allocations in the case where we find a value in the cache. + lazy = GetLazy(projectState); + } + + return await lazy.GetValueAsync(cancellationToken).ConfigureAwait(false); + + AsyncLazy GetLazy(ProjectState projectState) + => s_hasSourceGeneratorsMap.GetValue( + projectState, + projectState => AsyncLazy.Create( + static (tuple, cancellationToken) => ComputeHasSourceGeneratorsAsync(tuple.@this, tuple.projectState, cancellationToken), + (@this: this, projectState))); + + static async Task ComputeHasSourceGeneratorsAsync( + SolutionCompilationState solution, ProjectState projectState, CancellationToken cancellationToken) + { + var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); + // If in proc, just load the generators and see if we have any. + if (client is null) + return GetSourceGenerators(projectState).Any(); + + // Out of process, call to the remote to figure this out. + var projectId = projectState.Id; + var result = await client.TryInvokeAsync( + solution, + projectId, + (service, solution, cancellationToken) => service.HasGeneratorsAsync(solution, projectId, cancellationToken), + cancellationToken).ConfigureAwait(false); + return result.HasValue && result.Value; + } + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 3e6200977dde5..e8edb8f0569cd 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -42,12 +41,13 @@ internal sealed partial class SolutionState private readonly SolutionInfo.SolutionAttributes _solutionAttributes; private readonly ImmutableDictionary _projectIdToProjectStateMap; - private readonly FilePathToDocumentIdsMap _filePathToDocumentIdsMap; private readonly ProjectDependencyGraph _dependencyGraph; // holds on data calculated based on the AnalyzerReferences list private readonly Lazy _lazyAnalyzers; + private ImmutableDictionary> _lazyFilePathToRelatedDocumentIds = ImmutableDictionary>.Empty; + private SolutionState( string? workspaceKind, int workspaceVersion, @@ -57,7 +57,6 @@ private SolutionState( SolutionOptionSet options, IReadOnlyList analyzerReferences, ImmutableDictionary idToProjectStateMap, - FilePathToDocumentIdsMap filePathToDocumentIdsMap, ProjectDependencyGraph dependencyGraph, Lazy? lazyAnalyzers) { @@ -69,7 +68,6 @@ private SolutionState( Options = options; AnalyzerReferences = analyzerReferences; _projectIdToProjectStateMap = idToProjectStateMap; - _filePathToDocumentIdsMap = filePathToDocumentIdsMap; _dependencyGraph = dependencyGraph; _lazyAnalyzers = lazyAnalyzers ?? CreateLazyHostDiagnosticAnalyzers(analyzerReferences); @@ -100,7 +98,6 @@ public SolutionState( options, analyzerReferences, idToProjectStateMap: ImmutableDictionary.Empty, - filePathToDocumentIdsMap: FilePathToDocumentIdsMap.Empty, dependencyGraph: ProjectDependencyGraph.Empty, lazyAnalyzers: null) { @@ -152,7 +149,6 @@ internal SolutionState Branch( SolutionOptionSet? options = null, IReadOnlyList? analyzerReferences = null, ImmutableDictionary? idToProjectStateMap = null, - FilePathToDocumentIdsMap? filePathToDocumentIdsMap = null, ProjectDependencyGraph? dependencyGraph = null) { solutionAttributes ??= _solutionAttributes; @@ -160,7 +156,6 @@ internal SolutionState Branch( idToProjectStateMap ??= _projectIdToProjectStateMap; options ??= Options; analyzerReferences ??= AnalyzerReferences; - filePathToDocumentIdsMap ??= _filePathToDocumentIdsMap; dependencyGraph ??= _dependencyGraph; var analyzerReferencesEqual = AnalyzerReferences.SequenceEqual(analyzerReferences); @@ -170,7 +165,6 @@ internal SolutionState Branch( options == Options && analyzerReferencesEqual && idToProjectStateMap == _projectIdToProjectStateMap && - filePathToDocumentIdsMap == _filePathToDocumentIdsMap && dependencyGraph == _dependencyGraph) { return this; @@ -185,7 +179,6 @@ internal SolutionState Branch( options, analyzerReferences, idToProjectStateMap, - filePathToDocumentIdsMap.Value, dependencyGraph, analyzerReferencesEqual ? _lazyAnalyzers : null); } @@ -218,13 +211,10 @@ public SolutionState WithNewWorkspace( Options, AnalyzerReferences, _projectIdToProjectStateMap, - _filePathToDocumentIdsMap, _dependencyGraph, _lazyAnalyzers); } - public FilePathToDocumentIdsMap FilePathToDocumentIdsMap => _filePathToDocumentIdsMap; - /// /// The version of the most recently modified project. /// @@ -329,13 +319,10 @@ private SolutionState AddProject(ProjectState projectState) } } - var newFilePathToDocumentIdsMap = CreateFilePathToDocumentIdsMapWithAddedDocuments(GetDocumentStates(newStateMap[projectId])); - return Branch( solutionAttributes: newSolutionAttributes, projectIds: newProjectIds, idToProjectStateMap: newStateMap, - filePathToDocumentIdsMap: newFilePathToDocumentIdsMap, dependencyGraph: newDependencyGraph); } @@ -376,11 +363,6 @@ public SolutionState AddProject(ProjectInfo projectInfo) return this.AddProject(newProject); } - private static IEnumerable GetDocumentStates(ProjectState projectState) - => projectState.DocumentStates.States.Values - .Concat(projectState.AdditionalDocumentStates.States.Values) - .Concat(projectState.AnalyzerConfigDocumentStates.States.Values); - /// /// Create a new solution instance without the project specified. /// @@ -399,57 +381,14 @@ public SolutionState RemoveProject(ProjectId projectId) var newProjectIds = ProjectIds.ToImmutableArray().Remove(projectId); var newStateMap = _projectIdToProjectStateMap.Remove(projectId); var newDependencyGraph = _dependencyGraph.WithProjectRemoved(projectId); - var newFilePathToDocumentIdsMap = CreateFilePathToDocumentIdsMapWithRemovedDocuments(GetDocumentStates(_projectIdToProjectStateMap[projectId])); return this.Branch( solutionAttributes: newSolutionAttributes, projectIds: newProjectIds, idToProjectStateMap: newStateMap, - filePathToDocumentIdsMap: newFilePathToDocumentIdsMap, dependencyGraph: newDependencyGraph); } - public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithAddedDocuments(IEnumerable documentStates) - { - var builder = _filePathToDocumentIdsMap.ToBuilder(); - AddDocumentFilePaths(documentStates, builder); - return builder.ToImmutable(); - } - - private static void AddDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) - { - foreach (var documentState in documentStates) - builder.Add(documentState.FilePath, documentState.Id); - } - - public FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithRemovedDocuments(IEnumerable documentStates) - { - var builder = _filePathToDocumentIdsMap.ToBuilder(); - RemoveDocumentFilePaths(documentStates, builder); - return builder.ToImmutable(); - } - - private static void RemoveDocumentFilePaths(IEnumerable documentStates, FilePathToDocumentIdsMap.Builder builder) - { - foreach (var documentState in documentStates) - builder.Remove(documentState.FilePath, documentState.Id); - } - - private FilePathToDocumentIdsMap CreateFilePathToDocumentIdsMapWithFilePath(DocumentId documentId, string? oldFilePath, string? newFilePath) - { - if (oldFilePath == newFilePath) - { - return _filePathToDocumentIdsMap; - } - - var builder = _filePathToDocumentIdsMap.ToBuilder(); - - builder.Remove(oldFilePath, documentId); - builder.Add(newFilePath, documentId); - - return builder.ToImmutable(); - } - /// /// Creates a new solution instance with the project specified updated to have the new /// assembly name. @@ -1118,13 +1057,9 @@ private StateChange UpdateDocumentState(DocumentState newDocument, bool contentC // This method shouldn't have been called if the document has not changed. Debug.Assert(oldProject != newProject); - var oldDocument = oldProject.DocumentStates.GetRequiredState(newDocument.Id); - var newFilePathToDocumentIdsMap = CreateFilePathToDocumentIdsMapWithFilePath(newDocument.Id, oldDocument.FilePath, newDocument.FilePath); - return ForkProject( oldProject, - newProject, - newFilePathToDocumentIdsMap: newFilePathToDocumentIdsMap); + newProject); } private StateChange UpdateAdditionalDocumentState(AdditionalDocumentState newDocument, bool contentChanged) @@ -1158,8 +1093,7 @@ private StateChange UpdateAnalyzerConfigDocumentState(AnalyzerConfigDocumentStat public StateChange ForkProject( ProjectState oldProjectState, ProjectState newProjectState, - ProjectDependencyGraph? newDependencyGraph = null, - FilePathToDocumentIdsMap? newFilePathToDocumentIdsMap = null) + ProjectDependencyGraph? newDependencyGraph = null) { var projectId = newProjectState.Id; @@ -1170,8 +1104,7 @@ public StateChange ForkProject( var newSolutionState = this.Branch( idToProjectStateMap: newStateMap, - dependencyGraph: newDependencyGraph, - filePathToDocumentIdsMap: newFilePathToDocumentIdsMap ?? _filePathToDocumentIdsMap); + dependencyGraph: newDependencyGraph); return new(newSolutionState, oldProjectState, newProjectState); } @@ -1185,38 +1118,19 @@ public ImmutableArray GetDocumentIdsWithFilePath(string? filePath) if (string.IsNullOrEmpty(filePath)) return []; - if (!_filePathToDocumentIdsMap.TryGetValue(filePath, out var documentIds)) - return []; + return ImmutableInterlocked.GetOrAdd( + ref _lazyFilePathToRelatedDocumentIds, + filePath, + static (filePath, @this) => ComputeDocumentIdsWithFilePath(@this, filePath), + this); - // If this wasn't the result of a freeze, then we can return the document ids as is. They should be accurate. - if (!_filePathToDocumentIdsMap.IsFrozen) + static ImmutableArray ComputeDocumentIdsWithFilePath(SolutionState @this, string filePath) { - Debug.Assert(documentIds.All(ContainsAnyDocument)); - return documentIds; - } - - // We were frozen. So we may be seeing document ids that no longer exist within this snapshot. If so, - // filter them out. - using var _ = ArrayBuilder.GetInstance(documentIds.Length, out var result); + using var result = TemporaryArray.Empty; + foreach (var (projectId, projectState) in @this.ProjectStates) + projectState.AddDocumentIdsWithFilePath(ref result.AsRef(), filePath); - foreach (var documentId in documentIds) - { - if (ContainsAnyDocument(documentId)) - result.Add(documentId); - } - - result.RemoveDuplicates(); - return result.ToImmutableAndClear(); - - bool ContainsAnyDocument(DocumentId documentId) - { - var project = this.GetProjectState(documentId.ProjectId); - if (project is null) - return false; - - return project.DocumentStates.Contains(documentId) - || project.AdditionalDocumentStates.Contains(documentId) - || project.AnalyzerConfigDocumentStates.Contains(documentId); + return result.ToImmutableAndClear(); } } @@ -1269,6 +1183,59 @@ public SolutionState WithAnalyzerReferences(IReadOnlyList ana return Branch(analyzerReferences: analyzerReferences); } + public DocumentId? GetFirstRelatedDocumentId(DocumentId documentId, ProjectId? relatedProjectIdHint) + { + Contract.ThrowIfTrue(documentId.ProjectId == relatedProjectIdHint); + + var projectState = this.GetProjectState(documentId.ProjectId); + if (projectState is null) + return null; + + var documentState = projectState.DocumentStates.GetState(documentId); + if (documentState is null) + return null; + + var filePath = documentState.FilePath; + if (string.IsNullOrEmpty(filePath)) + return null; + + // Do a quick check if the full info for that path has already been computed and cached. + var fileMap = _lazyFilePathToRelatedDocumentIds; + if (fileMap != null && fileMap.TryGetValue(filePath, out var relatedDocumentIds)) + { + foreach (var relatedDocumentId in relatedDocumentIds) + { + if (relatedDocumentId != documentId) + return relatedDocumentId; + } + + return null; + } + + var relatedProject = relatedProjectIdHint is null ? null : this.ProjectStates[relatedProjectIdHint]; + Contract.ThrowIfTrue(relatedProject == projectState); + if (relatedProject != null) + { + var siblingDocumentId = relatedProject.GetFirstDocumentIdWithFilePath(filePath); + if (siblingDocumentId is not null) + return siblingDocumentId; + } + + // Wasn't in cache, do the linear search. + foreach (var (_, siblingProjectState) in this.ProjectStates) + { + // Don't want to search the same project that document already came from, or from the related-project we had a hint for. + if (siblingProjectState == projectState || siblingProjectState == relatedProject) + continue; + + var siblingDocumentId = siblingProjectState.GetFirstDocumentIdWithFilePath(filePath); + if (siblingDocumentId is not null) + return siblingDocumentId; + } + + return null; + } + public ImmutableArray GetRelatedDocumentIds(DocumentId documentId) { var projectState = this.GetProjectState(documentId.ProjectId); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocument.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocument.cs index f772be5a2ba1c..2a2a9d97a6668 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocument.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocument.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.Threading; namespace Microsoft.CodeAnalysis; @@ -23,6 +24,8 @@ internal SourceGeneratedDocument(Project project, SourceGeneratedDocumentState s // TODO: make this public. Tracked by https://github.com/dotnet/roslyn/issues/50546 internal SourceGeneratedDocumentIdentity Identity => State.Identity; + internal DateTime GenerationDateTime => State.GenerationDateTime; + internal override Document WithFrozenPartialSemantics(CancellationToken cancellationToken) { // For us to implement frozen partial semantics here with a source generated document, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index 81bfcb7a765e8..ef8496df08b37 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -38,12 +38,15 @@ internal sealed class SourceGeneratedDocumentState : DocumentState public Checksum GetOriginalSourceTextContentHash() => _lazyContentHash.Value; + public readonly DateTime GenerationDateTime; + public static SourceGeneratedDocumentState Create( SourceGeneratedDocumentIdentity documentIdentity, SourceText generatedSourceText, ParseOptions parseOptions, LanguageServices languageServices, - Checksum? originalSourceTextChecksum) + Checksum? originalSourceTextChecksum, + DateTime generationDateTime) { // If the caller explicitly provided us with the checksum for the source text, then we always defer to that. // This happens on the host side, when we are given the data computed by the OOP side. @@ -51,7 +54,7 @@ public static SourceGeneratedDocumentState Create( // If the caller didn't provide us with the checksum, then we'll compute it on demand. This happens on the OOP // side when we're actually producing the SG doc in the first place. var lazyTextChecksum = new Lazy(() => originalSourceTextChecksum ?? ComputeContentHash(generatedSourceText)); - return Create(documentIdentity, generatedSourceText, parseOptions, languageServices, lazyTextChecksum); + return Create(documentIdentity, generatedSourceText, parseOptions, languageServices, lazyTextChecksum, generationDateTime); } private static SourceGeneratedDocumentState Create( @@ -59,7 +62,8 @@ private static SourceGeneratedDocumentState Create( SourceText generatedSourceText, ParseOptions parseOptions, LanguageServices languageServices, - Lazy lazyTextChecksum) + Lazy lazyTextChecksum, + DateTime generationDateTime) { var loadTextOptions = new LoadTextOptions(generatedSourceText.ChecksumAlgorithm); var textAndVersion = TextAndVersion.Create(generatedSourceText, VersionStamp.Create()); @@ -88,7 +92,8 @@ private static SourceGeneratedDocumentState Create( generatedSourceText, loadTextOptions, treeSource, - lazyTextChecksum); + lazyTextChecksum, + generationDateTime); } private SourceGeneratedDocumentState( @@ -97,17 +102,19 @@ private SourceGeneratedDocumentState( IDocumentServiceProvider? documentServiceProvider, DocumentInfo.DocumentAttributes attributes, ParseOptions options, - ConstantTextAndVersionSource textSource, + ITextAndVersionSource textSource, SourceText text, LoadTextOptions loadTextOptions, AsyncLazy treeSource, - Lazy lazyContentHash) + Lazy lazyContentHash, + DateTime generationDateTime) : base(languageServices, documentServiceProvider, attributes, options, textSource, loadTextOptions, treeSource) { Identity = documentIdentity; SourceText = text; _lazyContentHash = lazyContentHash; + GenerationDateTime = generationDateTime; } private static Checksum ComputeContentHash(SourceText text) @@ -135,7 +142,8 @@ public SourceGeneratedDocumentState WithText(SourceText sourceText) ParseOptions, LanguageServices, // Just pass along the checksum for the new source text since we've already computed it. - newSourceTextChecksum); + newSourceTextChecksum, + GenerationDateTime); } public SourceGeneratedDocumentState WithParseOptions(ParseOptions parseOptions) @@ -150,7 +158,30 @@ public SourceGeneratedDocumentState WithParseOptions(ParseOptions parseOptions) parseOptions, LanguageServices, // We're just changing the parse options. So the checksum will remain as is. - _lazyContentHash); + _lazyContentHash, + GenerationDateTime); + } + + public SourceGeneratedDocumentState WithGenerationDateTime(DateTime generationDateTime) + { + // See if we can reuse this instance directly + if (this.GenerationDateTime == generationDateTime) + return this; + + // Copy over all state as-is. The generation time doesn't change any actual state (the same tree will be + // produced for example). + return new( + this.Identity, + this.LanguageServices, + this.Services, + this.Attributes, + this.ParseOptions, + this.TextAndVersionSource, + this.SourceText, + this.LoadTextOptions, + this.TreeSource!, + this._lazyContentHash, + generationDateTime); } /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs index e030b033f7275..37dbe0ddb57a5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs @@ -5,11 +5,13 @@ using System; using System.Collections.Frozen; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Serialization; @@ -19,7 +21,8 @@ internal sealed class SolutionCompilationStateChecksums public SolutionCompilationStateChecksums( Checksum solutionState, ChecksumCollection? frozenSourceGeneratedDocumentIdentities, - ChecksumsAndIds? frozenSourceGeneratedDocuments) + ChecksumsAndIds? frozenSourceGeneratedDocuments, + ImmutableArray frozenSourceGeneratedDocumentGenerationDateTimes) { // For the frozen source generated document info, we expect two either have both checksum collections or neither, and they // should both be the same length as there is a 1:1 correspondence between them. @@ -29,7 +32,10 @@ public SolutionCompilationStateChecksums( SolutionState = solutionState; FrozenSourceGeneratedDocumentIdentities = frozenSourceGeneratedDocumentIdentities; FrozenSourceGeneratedDocuments = frozenSourceGeneratedDocuments; + FrozenSourceGeneratedDocumentGenerationDateTimes = frozenSourceGeneratedDocumentGenerationDateTimes; + // note: intentionally not mixing in FrozenSourceGeneratedDocumentGenerationDateTimes as that is not part of the + // identity contract of this type. Checksum = Checksum.Create( SolutionState, FrozenSourceGeneratedDocumentIdentities?.Checksum ?? Checksum.Null, @@ -41,6 +47,9 @@ public SolutionCompilationStateChecksums( public ChecksumCollection? FrozenSourceGeneratedDocumentIdentities { get; } public ChecksumsAndIds? FrozenSourceGeneratedDocuments { get; } + // note: intentionally not part of the identity contract of this type. + public ImmutableArray FrozenSourceGeneratedDocumentGenerationDateTimes { get; } + public void AddAllTo(HashSet checksums) { checksums.AddIfNotNullChecksum(this.Checksum); @@ -61,6 +70,7 @@ public void Serialize(ObjectWriter writer) { this.FrozenSourceGeneratedDocumentIdentities.Value.WriteTo(writer); this.FrozenSourceGeneratedDocuments!.Value.WriteTo(writer); + writer.WriteArray(this.FrozenSourceGeneratedDocumentGenerationDateTimes, static (w, d) => w.WriteInt64(d.Ticks)); } } @@ -72,17 +82,20 @@ public static SolutionCompilationStateChecksums Deserialize(ObjectReader reader) var hasFrozenSourceGeneratedDocuments = reader.ReadBoolean(); ChecksumCollection? frozenSourceGeneratedDocumentIdentities = null; ChecksumsAndIds? frozenSourceGeneratedDocuments = null; + ImmutableArray frozenSourceGeneratedDocumentGenerationDateTimes = default; if (hasFrozenSourceGeneratedDocuments) { frozenSourceGeneratedDocumentIdentities = ChecksumCollection.ReadFrom(reader); frozenSourceGeneratedDocuments = ChecksumsAndIds.ReadFrom(reader); + frozenSourceGeneratedDocumentGenerationDateTimes = reader.ReadArray(r => new DateTime(r.ReadInt64())); } var result = new SolutionCompilationStateChecksums( solutionState: solutionState, - frozenSourceGeneratedDocumentIdentities: frozenSourceGeneratedDocumentIdentities, - frozenSourceGeneratedDocuments: frozenSourceGeneratedDocuments); + frozenSourceGeneratedDocumentIdentities, + frozenSourceGeneratedDocuments, + frozenSourceGeneratedDocumentGenerationDateTimes); Contract.ThrowIfFalse(result.Checksum == checksum); return result; } @@ -103,12 +116,12 @@ public async Task FindAsync( if (searchingChecksumsLeft.Remove(Checksum)) result[Checksum] = this; - if (compilationState.FrozenSourceGeneratedDocumentStates.HasValue) + if (compilationState.FrozenSourceGeneratedDocumentStates != null) { Contract.ThrowIfFalse(FrozenSourceGeneratedDocumentIdentities.HasValue); // This could either be the checksum for the text (which we'll use our regular helper for first)... - await ChecksumCollection.FindAsync(compilationState.FrozenSourceGeneratedDocumentStates.Value, assetHint.DocumentId, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await ChecksumCollection.FindAsync(compilationState.FrozenSourceGeneratedDocumentStates, assetHint.DocumentId, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); // ... or one of the identities. In this case, we'll use the fact that there's a 1:1 correspondence between the // two collections we hold onto. @@ -118,7 +131,7 @@ public async Task FindAsync( if (searchingChecksumsLeft.Remove(identityChecksum)) { var id = FrozenSourceGeneratedDocuments!.Value.Ids[i]; - Contract.ThrowIfFalse(compilationState.FrozenSourceGeneratedDocumentStates.Value.TryGetState(id, out var state)); + Contract.ThrowIfFalse(compilationState.FrozenSourceGeneratedDocumentStates.TryGetState(id, out var state)); result[identityChecksum] = state.Identity; } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs index 781027bee1db2..4dea1bb700f59 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentStates.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -13,6 +14,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -20,11 +22,11 @@ namespace Microsoft.CodeAnalysis; /// /// Holds on a to map and an ordering. /// -internal readonly struct TextDocumentStates +internal sealed class TextDocumentStates where TState : TextDocumentState { public static readonly TextDocumentStates Empty = - new([], ImmutableSortedDictionary.Create(DocumentIdComparer.Instance)); + new([], ImmutableSortedDictionary.Create(DocumentIdComparer.Instance), FrozenDictionary>.Empty); private readonly ImmutableList _ids; @@ -35,28 +37,36 @@ internal readonly struct TextDocumentStates /// private readonly ImmutableSortedDictionary _map; - private TextDocumentStates(ImmutableList ids, ImmutableSortedDictionary map) + private FrozenDictionary>? _filePathToDocumentIds; + + private TextDocumentStates( + ImmutableList ids, + ImmutableSortedDictionary map, + FrozenDictionary>? filePathToDocumentIds) { Debug.Assert(map.KeyComparer == DocumentIdComparer.Instance); _ids = ids; _map = map; + _filePathToDocumentIds = filePathToDocumentIds; } public TextDocumentStates(IEnumerable states) : this(states.Select(s => s.Id).ToImmutableList(), - states.ToImmutableSortedDictionary(state => state.Id, state => state, DocumentIdComparer.Instance)) + states.ToImmutableSortedDictionary(state => state.Id, state => state, DocumentIdComparer.Instance), + filePathToDocumentIds: null) { } public TextDocumentStates(IEnumerable infos, Func stateConstructor) : this(infos.Select(info => info.Id).ToImmutableList(), - infos.ToImmutableSortedDictionary(info => info.Id, stateConstructor, DocumentIdComparer.Instance)) + infos.ToImmutableSortedDictionary(info => info.Id, stateConstructor, DocumentIdComparer.Instance), + filePathToDocumentIds: null) { } public TextDocumentStates WithCompilationOrder(ImmutableList ids) - => new(ids, _map); + => new(ids, _map, _filePathToDocumentIds); public int Count => _map.Count; @@ -79,7 +89,7 @@ public TState GetRequiredState(DocumentId documentId) /// /// s in the order in which they were added to the project (the compilation order). /// - public readonly IReadOnlyList Ids => _ids; + public IReadOnlyList Ids => _ids; /// /// States ordered by . @@ -138,7 +148,8 @@ public async ValueTask> SelectAsArrayAsync( public TextDocumentStates AddRange(ImmutableArray states) => new(_ids.AddRange(states.Select(state => state.Id)), - _map.AddRange(states.Select(state => KeyValuePairUtil.Create(state.Id, state)))); + _map.AddRange(states.Select(state => KeyValuePairUtil.Create(state.Id, state))), + filePathToDocumentIds: null); public TextDocumentStates RemoveRange(ImmutableArray ids) { @@ -158,22 +169,38 @@ public TextDocumentStates RemoveRange(ImmutableArray ids) } IEnumerable enumerableIds = ids; - return new(_ids.RemoveRange(enumerableIds), _map.RemoveRange(enumerableIds)); + return new(_ids.RemoveRange(enumerableIds), _map.RemoveRange(enumerableIds), filePathToDocumentIds: null); } internal TextDocumentStates SetState(DocumentId id, TState state) - => new(_ids, _map.SetItem(id, state)); + { + var oldState = _map[id]; + var filePathToDocumentIds = oldState.FilePath != state.FilePath + ? null + : _filePathToDocumentIds; + + return new(_ids, _map.SetItem(id, state), filePathToDocumentIds); + } public TextDocumentStates UpdateStates(Func transformation, TArg arg) { var builder = _map.ToBuilder(); - + var filePathsChanged = false; foreach (var (id, state) in _map) { - builder[id] = transformation(state, arg); + var newState = transformation(state, arg); + + // Track if the file path changed when updating any of the state values. + filePathsChanged = filePathsChanged || newState.FilePath != state.FilePath; + + builder[id] = newState; } - return new(_ids, builder.ToImmutable()); + // If any file paths changed, don't pass along our computed map. We'll recompute it on demand when needed. + var filePaths = filePathsChanged + ? null + : _filePathToDocumentIds; + return new(_ids, builder.ToImmutable(), filePaths); } /// @@ -270,4 +297,45 @@ public async ValueTask> GetChecksumsAndIdsAsync(Canc var documentChecksums = new ChecksumCollection(await documentChecksumTasks.WhenAll().ConfigureAwait(false)); return new(documentChecksums, SelectAsArray(static s => s.Id)); } + + public void AddDocumentIdsWithFilePath(ref TemporaryArray temporaryArray, string filePath) + { + // Lazily initialize the file path map if not computed. + _filePathToDocumentIds ??= ComputeFilePathToDocumentIds(); + + if (_filePathToDocumentIds.TryGetValue(filePath, out var oneOrMany)) + { + foreach (var value in oneOrMany) + temporaryArray.Add(value); + } + } + + public DocumentId? GetFirstDocumentIdWithFilePath(string filePath) + { + // Lazily initialize the file path map if not computed. + _filePathToDocumentIds ??= ComputeFilePathToDocumentIds(); + + // Safe to call .First here as the values in the _filePathToDocumentIds dictionary will never empty. + return _filePathToDocumentIds.TryGetValue(filePath, out var oneOrMany) + ? oneOrMany.First() + : null; + } + + private FrozenDictionary> ComputeFilePathToDocumentIds() + { + using var _ = PooledDictionary>.GetInstance(out var result); + + foreach (var (documentId, state) in _map) + { + var filePath = state.FilePath; + if (filePath is null) + continue; + + result[filePath] = result.TryGetValue(filePath, out var existingValue) + ? existingValue.Add(documentId) + : OneOrMany.Create(documentId); + } + + return result.ToFrozenDictionary(); + } } diff --git a/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs b/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs index d3251442bd3e4..da91f2c6f1a09 100644 --- a/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs +++ b/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs @@ -22,18 +22,16 @@ public static ImmutableArray GetRelatedDocumentsWithChanges(this Sourc { var documentId = workspace.GetDocumentIdInCurrentContext(text.Container); if (documentId == null) - { return []; - } var solution = workspace.CurrentSolution; - if (workspace.TryGetOpenSourceGeneratedDocumentIdentity(documentId, out var documentIdentity)) + if (workspace.TryGetOpenSourceGeneratedDocumentIdentity(documentId, out var identityAndDateTime)) { // For source generated documents, we won't count them as linked across multiple projects; this is because // the generated documents in each target may have different source so other features might be surprised if we // return the same documents but with different text. So in this case, we'll just return a single document. - return [solution.WithFrozenSourceGeneratedDocument(documentIdentity, text)]; + return [solution.WithFrozenSourceGeneratedDocument(identityAndDateTime.identity, identityAndDateTime.generationDateTime, text)]; } var relatedIds = solution.GetRelatedDocumentIds(documentId); @@ -65,14 +63,10 @@ public static ImmutableArray GetRelatedDocumentsWithChanges(this Sourc var solution = workspace.CurrentSolution; var id = workspace.GetDocumentIdInCurrentContext(text.Container); if (id == null) - { return null; - } - if (workspace.TryGetOpenSourceGeneratedDocumentIdentity(id, out var documentIdentity)) - { - return solution.WithFrozenSourceGeneratedDocument(documentIdentity, text); - } + if (workspace.TryGetOpenSourceGeneratedDocumentIdentity(id, out var identityAndDateTime)) + return solution.WithFrozenSourceGeneratedDocument(identityAndDateTime.identity, identityAndDateTime.generationDateTime, text); if (solution.ContainsDocument(id)) { diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index b75aad7b448bc..2c80214993263 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -283,8 +283,12 @@ static Solution UnifyLinkedDocumentContents(Solution oldSolution, Solution newSo if (!addedProject.SupportsCompilation) continue; + // It's likely when adding files that if we link them to files in another project, that we will do the + // same for other sibling files being added. Keep that information around so help speed up the linked + // file search as we process siblings. + ProjectId? relatedProjectIdHint = null; foreach (var addedDocument in addedProject.Documents) - newSolution = UpdateAddedDocumentToExistingContentsInSolution(newSolution, addedDocument.Id); + (newSolution, relatedProjectIdHint) = UpdateAddedDocumentToExistingContentsInSolution(newSolution, addedDocument.Id, relatedProjectIdHint); } using var _ = PooledHashSet.GetInstance(out var seenChangedDocuments); @@ -296,8 +300,9 @@ static Solution UnifyLinkedDocumentContents(Solution oldSolution, Solution newSo continue; // Now do the same for all added documents in a project. + ProjectId? relatedProjectIdHint = null; foreach (var addedDocument in projectChanges.GetAddedDocuments()) - newSolution = UpdateAddedDocumentToExistingContentsInSolution(newSolution, addedDocument); + (newSolution, relatedProjectIdHint) = UpdateAddedDocumentToExistingContentsInSolution(newSolution, addedDocument, relatedProjectIdHint); // now, for any changed document, ensure we go and make all links to it have the same text/tree. foreach (var changedDocumentId in projectChanges.GetChangedDocuments()) @@ -307,16 +312,33 @@ static Solution UnifyLinkedDocumentContents(Solution oldSolution, Solution newSo return newSolution; } - static Solution UpdateAddedDocumentToExistingContentsInSolution(Solution solution, DocumentId addedDocumentId) + static (Solution newSolution, ProjectId? relatedProjectId) UpdateAddedDocumentToExistingContentsInSolution( + Solution solution, DocumentId addedDocumentId, ProjectId? relatedProjectIdHint) { - var relatedDocumentIds = solution.GetRelatedDocumentIds(addedDocumentId); - foreach (var relatedDocumentId in relatedDocumentIds) - { - var relatedDocument = solution.GetRequiredDocument(relatedDocumentId); - return solution.WithDocumentContentsFrom(addedDocumentId, relatedDocument.DocumentState, forceEvenIfTreesWouldDiffer: false); - } + Contract.ThrowIfTrue(addedDocumentId.ProjectId == relatedProjectIdHint); - return solution; + // Look for a related document we can create our contents from. We only have to look for a single related + // doc as we'll be done once we update our contents to theirs. Note: GetFirstRelatedDocumentId will also + // not search the project that addedDocumentId came from. So this will help ensure we don't repeatedly add + // documents to a project, then look for related docs *within that project*, forcing the file-path map in it + // to be recreated for each document. + var relatedDocumentId = solution.GetFirstRelatedDocumentId(addedDocumentId, relatedProjectIdHint); + + // Couldn't find a related document. Keep the same solution, and keep track of the best related project we + // found while processing this project. + if (relatedDocumentId is null) + return (solution, relatedProjectIdHint); + + var relatedDocument = solution.GetRequiredDocument(relatedDocumentId); + + // Should never return a file as its own related document + Contract.ThrowIfTrue(relatedDocumentId == addedDocumentId); + + // Related document must come from a distinct project. + Contract.ThrowIfTrue(relatedDocumentId.ProjectId == addedDocumentId.ProjectId); + + var newSolution = solution.WithDocumentContentsFrom(addedDocumentId, relatedDocument.DocumentState, forceEvenIfTreesWouldDiffer: false); + return (newSolution, relatedProjectId: relatedDocumentId.ProjectId); } static Solution UpdateExistingDocumentsToChangedDocumentContents(Solution solution, DocumentId changedDocumentId, HashSet processedDocuments) @@ -329,6 +351,9 @@ static Solution UpdateExistingDocumentsToChangedDocumentContents(Solution soluti var relatedDocumentIds = solution.GetRelatedDocumentIds(changedDocumentId); foreach (var relatedDocumentId in relatedDocumentIds) { + if (relatedDocumentId == changedDocumentId) + continue; + if (processedDocuments.Add(relatedDocumentId)) solution = solution.WithDocumentContentsFrom(relatedDocumentId, changedDocument.DocumentState, forceEvenIfTreesWouldDiffer: false); } diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs index 86f36f622bfa5..81280aab6c2bd 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs @@ -24,5 +24,6 @@ public static class WorkspaceKind internal const string CloudEnvironmentClientWorkspace = nameof(CloudEnvironmentClientWorkspace); internal const string RemoteWorkspace = nameof(RemoteWorkspace); + internal const string SemanticSearch = nameof(SemanticSearch); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 811d5f448be43..b03867cd15a55 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -39,7 +39,7 @@ public abstract partial class Workspace private readonly Dictionary _textTrackers = []; private readonly Dictionary _documentToAssociatedBufferMap = []; - private readonly Dictionary _openSourceGeneratedDocumentIdentities = []; + private readonly Dictionary _openSourceGeneratedDocumentIdentities = []; /// /// True if this workspace supports manually opening and closing documents. @@ -287,12 +287,10 @@ internal DocumentId GetDocumentIdInCurrentContext(DocumentId documentId) return _bufferToAssociatedDocumentsMap.Where(kvp => kvp.Value.Contains(documentId)).Select(kvp => kvp.Key).FirstOrDefault(); } - internal bool TryGetOpenSourceGeneratedDocumentIdentity(DocumentId id, out SourceGeneratedDocumentIdentity documentIdentity) + internal bool TryGetOpenSourceGeneratedDocumentIdentity(DocumentId id, out (SourceGeneratedDocumentIdentity identity, DateTime generationDateTime) documentIdentity) { using (_serializationLock.DisposableWait()) - { return _openSourceGeneratedDocumentIdentities.TryGetValue(id, out documentIdentity); - } } /// @@ -456,7 +454,7 @@ internal void OnSourceGeneratedDocumentOpened( AddToOpenDocumentMap(documentId); _documentToAssociatedBufferMap.Add(documentId, textContainer); - _openSourceGeneratedDocumentIdentities.Add(documentId, document.Identity); + _openSourceGeneratedDocumentIdentities.Add(documentId, (document.Identity, document.GenerationDateTime)); UpdateCurrentContextMapping_NoLock(textContainer, documentId, isCurrentContext: true); diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf index 01a6177cdbf0a..93569dd7abbc2 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + Příkaz CodeAction {0} nevytvořil změněné řešení. "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf index 1f0421533b60b..4dbc4bbf1268c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + Durch CodeAction "{0}" wurde keine geänderte Lösung erstellt. "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf index 2210f10672a40..d79f326dae933 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + El tipo CodeAction "{0}" no generó una solución modificada "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf index bb45ae4e0a334..4437971ba7614 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + Le CodeAction '{0}' n'a pas produit de solution contenant des changements "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf index 2859f6de346ec..77eab2b223826 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + L'elemento CodeAction '{0}' non ha generato una soluzione modificata "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf index 71c364b2a340f..7f16149e410ce 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + CodeAction '{0}' は変更されたソリューションを生成しませんでした "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf index 610a8d087a4a6..ab6a3d0da4853 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + CodeAction '{0}'이(가) 변경된 솔루션을 생성하지 않았습니다. "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf index 978ac0bbe0bc0..5d6df9750c27b 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + Element CodeAction „{0}” nie utworzył zmienionego rozwiązania "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf index 0c2903fd16493..d61231dbb8017 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + A CodeAction '{0}' não produziu uma solução alterada "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf index 0795ab056bb85..a59fe061c71d0 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + Действие кода "{0}" не сформировало измененное решение. "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf index 90339083761b3..a27884ea21b81 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + '{0}' CodeAction, değiştirilmiş çözüm üretmedi "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf index f08d483010a6a..282030d47ddc5 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + CodeAction "{0}" 未生成更改的解决方案 "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf index b9a09d50a4d87..fa3e4431600d7 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf @@ -39,7 +39,7 @@ CodeAction '{0}' did not produce a changed solution - CodeAction '{0}' did not produce a changed solution + CodeAction '{0}' 未產生變更的解決方案 "CodeAction" is a specific type, and {0} represents the title shown by the action. diff --git a/src/Workspaces/CoreTest/ChecksumTests.cs b/src/Workspaces/CoreTest/ChecksumTests.cs index e56f177d1092f..fe1abf9386512 100644 --- a/src/Workspaces/CoreTest/ChecksumTests.cs +++ b/src/Workspaces/CoreTest/ChecksumTests.cs @@ -49,6 +49,30 @@ public void ValidateChecksumFromSpanSameAsChecksumFromBytes2() Assert.NotEqual(checksum3, checksumA); } + [Fact] + public void ValidateChecksumFromSpanSameAsChecksumFromBytes3() + { + var checksum1 = Checksum.Create("Goo"); + var checksum2 = Checksum.Create("Bar"); + var checksum3 = Checksum.Create("Baz"); + var checksum4 = Checksum.Create("Quux"); + + var checksumA = Checksum.Create(checksum1, checksum2, checksum3, checksum4); + + // Running this test on multiple target frameworks with the same expectation ensures the results match + Assert.Equal(Checksum.FromBase64String("vva1KeNW7vz7PNnIyM3K6g=="), checksumA); + + Assert.NotEqual(checksum1, checksum2); + Assert.NotEqual(checksum2, checksum3); + Assert.NotEqual(checksum3, checksum4); + Assert.NotEqual(checksum4, checksum1); + + Assert.NotEqual(checksum1, checksumA); + Assert.NotEqual(checksum2, checksumA); + Assert.NotEqual(checksum3, checksumA); + Assert.NotEqual(checksum4, checksumA); + } + [Fact] public void ValidateChecksumFromSpanSameAsChecksumFromBytes10() { diff --git a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs index aef8ea9dcbb37..3242dead125c5 100644 --- a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs +++ b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs @@ -53,13 +53,13 @@ public class ServiceDescriptorTests { public static IEnumerable AllServiceDescriptors => ServiceDescriptors.Instance.GetTestAccessor().Descriptors - .Select(descriptor => new object[] { descriptor.Key, descriptor.Value.descriptor64, descriptor.Value.descriptor64ServerGC, descriptor.Value.descriptorCoreClr64, descriptor.Value.descriptorCoreClr64ServerGC }); + .Select(descriptor => new object[] { descriptor.Key, descriptor.Value.descriptorCoreClr64, descriptor.Value.descriptorCoreClr64ServerGC }); private static Dictionary GetAllParameterTypesOfRemoteApis() { var interfaces = new List(); - foreach (var (serviceType, (descriptor, _, _, _)) in ServiceDescriptors.Instance.GetTestAccessor().Descriptors) + foreach (var (serviceType, (descriptor, _)) in ServiceDescriptors.Instance.GetTestAccessor().Descriptors) { interfaces.Add(serviceType); if (descriptor.ClientInterface != null) @@ -363,20 +363,16 @@ public void TypesUsedInRemoteApisMustBeMessagePackSerializable() [MemberData(nameof(AllServiceDescriptors))] internal void GetFeatureDisplayName( Type serviceInterface, - ServiceDescriptor descriptor64, - ServiceDescriptor descriptor64ServerGC, ServiceDescriptor descriptorCoreClr64, ServiceDescriptor descriptorCoreClr64ServerGC) { Assert.NotNull(serviceInterface); - var expectedName = descriptor64.GetFeatureDisplayName(); + var expectedName = descriptorCoreClr64.GetFeatureDisplayName(); // The service name couldn't be found. It may need to be added to RemoteWorkspacesResources.resx as FeatureName_{name} Assert.False(string.IsNullOrEmpty(expectedName), $"Service name for '{serviceInterface.GetType()}' not available."); - Assert.Equal(expectedName, descriptor64ServerGC.GetFeatureDisplayName()); - Assert.Equal(expectedName, descriptorCoreClr64.GetFeatureDisplayName()); Assert.Equal(expectedName, descriptorCoreClr64ServerGC.GetFeatureDisplayName()); } @@ -387,7 +383,7 @@ public void CallbackDispatchers() var callbackDispatchers = ((IMefHostExportProvider)hostServices).GetExports(); var descriptorsWithCallbackServiceTypes = ServiceDescriptors.Instance.GetTestAccessor().Descriptors - .Where(d => d.Value.descriptor64.ClientInterface != null).Select(d => d.Key); + .Where(d => d.Value.descriptorCoreClr64.ClientInterface != null).Select(d => d.Key); var callbackDispatcherServiceTypes = callbackDispatchers.Select(d => d.Metadata.ServiceInterface); AssertEx.SetEqual(descriptorsWithCallbackServiceTypes, callbackDispatcherServiceTypes); diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index 1a5d986601cff..38f5b447fbe39 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -834,7 +834,8 @@ public async Task FreezingSourceGeneratedDocumentsWorks(TestHost testHost) static async Task AssertFrozen(Project project, SourceGeneratedDocumentIdentity identity) { - var frozenWithSingleDocument = project.Solution.WithFrozenSourceGeneratedDocument(identity, SourceText.From("// Frozen Document")); + var frozenWithSingleDocument = project.Solution.WithFrozenSourceGeneratedDocument( + identity, DateTime.Now, SourceText.From("// Frozen Document")); Assert.Equal("// Frozen Document", (await frozenWithSingleDocument.GetTextAsync()).ToString()); var frozenTree = Assert.Single((await frozenWithSingleDocument.Project.GetRequiredCompilationAsync(CancellationToken.None)).SyntaxTrees); Assert.Equal("// Frozen Document", frozenTree.ToString()); @@ -860,7 +861,7 @@ public async Task FreezingSourceGeneratedDocumentsInTwoProjectsWorks(TestHost te // And now freeze both of them at once var solutionWithFrozenDocuments = solution.WithFrozenSourceGeneratedDocuments( - [(sourceGeneratedDocument1.Identity, SourceText.From("// Frozen 1")), (sourceGeneratedDocument2.Identity, SourceText.From("// Frozen 2"))]); + [(sourceGeneratedDocument1.Identity, DateTime.Now, SourceText.From("// Frozen 1")), (sourceGeneratedDocument2.Identity, DateTime.Now, SourceText.From("// Frozen 2"))]); Assert.Equal("// Frozen 1", (await (await solutionWithFrozenDocuments.GetRequiredProject(projectId1).GetSourceGeneratedDocumentsAsync()).Single().GetTextAsync()).ToString()); Assert.Equal("// Frozen 2", (await (await solutionWithFrozenDocuments.GetRequiredProject(projectId2).GetSourceGeneratedDocumentsAsync()).Single().GetTextAsync()).ToString()); @@ -879,7 +880,8 @@ public async Task FreezingWithSameContentDoesNotFork(TestHost testHost) var sourceGeneratedDocument = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()); var sourceGeneratedDocumentIdentity = sourceGeneratedDocument.Identity; - var frozenSolution = project.Solution.WithFrozenSourceGeneratedDocument(sourceGeneratedDocumentIdentity, SourceText.From("// Hello, World")); + var frozenSolution = project.Solution.WithFrozenSourceGeneratedDocument( + sourceGeneratedDocumentIdentity, sourceGeneratedDocument.GenerationDateTime, SourceText.From("// Hello, World")); Assert.Same(project.Solution, frozenSolution.Project.Solution); } } diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index d6438a07d9c5a..d67b4ad55559e 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -59,7 +59,7 @@ public RemoteWorkspace GetRemoteWorkspace() public override RemoteServiceConnection CreateConnection(object? callbackTarget) where T : class { - var descriptor = ServiceDescriptors.Instance.GetServiceDescriptor(typeof(T), RemoteProcessConfiguration.ServerGC | (ServiceDescriptors.IsCurrentProcessRunningOnCoreClr() ? RemoteProcessConfiguration.Core : 0)); + var descriptor = ServiceDescriptors.Instance.GetServiceDescriptor(typeof(T), RemoteProcessConfiguration.ServerGC); var callbackDispatcher = (descriptor.ClientInterface != null) ? _callbackDispatchers.GetDispatcher(typeof(T)) : null; return new BrokeredServiceConnection( @@ -212,6 +212,7 @@ public InProcRemoteServices(SolutionServices workspaceServices, TraceListener? t RegisterRemoteBrokeredService(new RemoteStackTraceExplorerService.Factory()); RegisterRemoteBrokeredService(new RemoteUnitTestingSearchService.Factory()); RegisterRemoteBrokeredService(new RemoteSourceGenerationService.Factory()); + RegisterRemoteBrokeredService(new RemoteSemanticSearchService.Factory()); } public void Dispose() diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs index dc823e1820886..ef18057196fcd 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs @@ -67,6 +67,7 @@ public partial class TestWorkspace private const string CommonReferencesNetCoreAppName = "CommonReferencesNetCoreApp"; private const string CommonReferencesNet6Name = "CommonReferencesNet6"; private const string CommonReferencesNet7Name = "CommonReferencesNet7"; + private const string CommonReferencesNet8Name = "CommonReferencesNet8"; private const string CommonReferencesNetStandard20Name = "CommonReferencesNetStandard20"; private const string CommonReferencesMinCorlibName = "CommonReferencesMinCorlib"; private const string ReferencesOnDiskAttributeName = "ReferencesOnDisk"; @@ -928,6 +929,14 @@ private IList CreateCommonReferences(XElement element) references = TargetFrameworkUtil.GetReferences(TargetFramework.Net70).ToList(); } + var net8 = element.Attribute(CommonReferencesNet8Name); + if (net8 != null && + ((bool?)net8).HasValue && + ((bool?)net8).Value) + { + references = TargetFrameworkUtil.GetReferences(TargetFramework.Net80).ToList(); + } + var mincorlib = element.Attribute(CommonReferencesMinCorlibName); if (mincorlib != null && ((bool?)mincorlib).HasValue && diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs index 95a0a2d362888..309159decf2be 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace`1.cs @@ -347,6 +347,7 @@ private bool KindSupportsAddRemoveDocument() { WorkspaceKind.MiscellaneousFiles => false, WorkspaceKind.Interactive => false, + WorkspaceKind.SemanticSearch => false, _ => true }; diff --git a/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs b/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs index 71dfab8be7bec..100a69202bfb6 100644 --- a/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/BrokeredServiceDescriptors.cs @@ -4,6 +4,7 @@ using System; using Microsoft.ServiceHub.Framework; +using Nerdbank.Streams; using StreamJsonRpc; using static Microsoft.ServiceHub.Framework.ServiceJsonRpcDescriptor; @@ -71,6 +72,9 @@ protected override JsonRpcConnection CreateConnection(JsonRpc jsonRpc) public static readonly ServiceRpcDescriptor SolutionSnapshotProvider = CreateClientServiceDescriptor("SolutionSnapshotProvider", new Version(0, 1)); public static readonly ServiceRpcDescriptor DebuggerManagedHotReloadService = CreateDebuggerServiceDescriptor("ManagedHotReloadService", new Version(0, 1)); public static readonly ServiceRpcDescriptor HotReloadLoggerService = CreateDebuggerServiceDescriptor("HotReloadLogger", new Version(0, 1)); + public static readonly ServiceRpcDescriptor HotReloadSessionNotificationService = CreateDebuggerServiceDescriptor("HotReloadSessionNotificationService", new Version(0, 1)); + public static readonly ServiceRpcDescriptor ManagedHotReloadAgentManagerService = CreateDebuggerServiceDescriptor("ManagedHotReloadAgentManagerService", new Version(0, 1)); + public static readonly ServiceRpcDescriptor MauiLaunchCustomizerServiceDescriptor = CreateMauiServiceDescriptor("MauiLaunchCustomizerService", new Version(0, 1)); public static ServiceMoniker CreateMoniker(string namespaceName, string componentName, string serviceName, Version? version) => new(namespaceName + "." + componentName + "." + serviceName, version); @@ -78,22 +82,31 @@ public static ServiceMoniker CreateMoniker(string namespaceName, string componen /// /// Descriptor for services proferred by the client extension (implemented in TypeScript). /// - public static ServiceJsonRpcDescriptor CreateClientServiceDescriptor(string serviceName, Version? version) + public static ServiceJsonRpcDescriptor CreateClientServiceDescriptor(string serviceName, Version? version = null) => new ClientServiceDescriptor(CreateMoniker(LanguageServerComponentNamespace, LanguageClientComponentName, serviceName, version), clientInterface: null) .WithExceptionStrategy(ExceptionProcessing.ISerializable); /// - /// Descriptor for services proferred by Roslyn server (implemented in C#). + /// Descriptor for services proferred by Roslyn server or Visual Studio in-proc (implemented in C#). /// - public static ServiceJsonRpcDescriptor CreateServerServiceDescriptor(string serviceName, Version? version) + public static ServiceJsonRpcDescriptor CreateServerServiceDescriptor(string serviceName, Version? version = null) => CreateDescriptor(CreateMoniker(LanguageServerComponentNamespace, LanguageServerComponentName, serviceName, version)); /// /// Descriptor for services proferred by the debugger server (implemented in C#). /// - public static ServiceJsonRpcDescriptor CreateDebuggerServiceDescriptor(string serviceName, Version? version) + public static ServiceJsonRpcDescriptor CreateDebuggerServiceDescriptor(string serviceName, Version? version = null) => CreateDescriptor(CreateMoniker(VisualStudioComponentNamespace, DebuggerComponentName, serviceName, version)); + /// + /// Descriptor for services proferred by the MAUI extension (implemented in TypeScript). + /// + public static ServiceJsonRpcDescriptor CreateMauiServiceDescriptor(string serviceName, Version? version) + => new ServiceJsonRpcDescriptor(CreateMoniker(VisualStudioComponentNamespace, "Maui", serviceName, version), clientInterface: null, + Formatters.MessagePack, MessageDelimiters.BigEndianInt32LengthHeader, + new MultiplexingStream.Options { ProtocolMajorVersion = 3 }) + .WithExceptionStrategy(ExceptionProcessing.ISerializable); + private static ServiceJsonRpcDescriptor CreateDescriptor(ServiceMoniker moniker) => new ServiceJsonRpcDescriptor(moniker, Formatters.MessagePack, MessageDelimiters.BigEndianInt32LengthHeader) .WithExceptionStrategy(ExceptionProcessing.ISerializable); diff --git a/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageServiceDescriptor.cs b/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageServiceDescriptor.cs new file mode 100644 index 0000000000000..cbc2270653ac9 --- /dev/null +++ b/src/Workspaces/Remote/Core/EditAndContinue/ManagedHotReloadLanguageServiceDescriptor.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using Microsoft.CodeAnalysis.BrokeredServices; +using Microsoft.ServiceHub.Framework; + +namespace Microsoft.CodeAnalysis.EditAndContinue; + +internal static class ManagedHotReloadLanguageServiceDescriptor +{ + private const string ServiceName = "ManagedHotReloadLanguageService"; + public const string ServiceVersion = "0.1"; + public const string MonikerName = BrokeredServiceDescriptors.LanguageServerComponentNamespace + "." + BrokeredServiceDescriptors.LanguageServerComponentName + "." + ServiceName; + + public static readonly ServiceJsonRpcDescriptor Descriptor = BrokeredServiceDescriptors.CreateServerServiceDescriptor(ServiceName, new(ServiceVersion)); + + static ManagedHotReloadLanguageServiceDescriptor() + => Debug.Assert(Descriptor.Moniker.Name == MonikerName); +} diff --git a/src/Workspaces/Remote/Core/IOnServiceBrokerInitialized.cs b/src/Workspaces/Remote/Core/IOnServiceBrokerInitialized.cs new file mode 100644 index 0000000000000..b47826b50f630 --- /dev/null +++ b/src/Workspaces/Remote/Core/IOnServiceBrokerInitialized.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.ServiceHub.Framework; + +namespace Microsoft.CodeAnalysis.BrokeredServices; + +/// +/// Allow services to export IOnServiceBrokerInitialized and getting called back when service broker is initialized +/// +internal interface IOnServiceBrokerInitialized +{ + void OnServiceBrokerInitialized(IServiceBroker serviceBroker); +} diff --git a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs index 0b3c0f3a0bc77..455c83c4177ed 100644 --- a/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/Core/IRemoteAssetSynchronizationService.cs @@ -7,15 +7,14 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; -namespace Microsoft.CodeAnalysis.Remote +namespace Microsoft.CodeAnalysis.Remote; + +internal interface IRemoteAssetSynchronizationService { - internal interface IRemoteAssetSynchronizationService - { - /// - /// Synchronize data to OOP proactively so that the corresponding solution is often already available when - /// features call into it. - /// - ValueTask SynchronizePrimaryWorkspaceAsync(Checksum solutionChecksum, int workspaceVersion, CancellationToken cancellationToken); - ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextChecksum, IEnumerable textChanges, CancellationToken cancellationToken); - } + /// + /// Synchronize data to OOP proactively so that the corresponding solution is often already available when features + /// call into it. + /// + ValueTask SynchronizePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken); + ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextChecksum, IEnumerable textChanges, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj b/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj index 2a2b29384f61b..05f63516078fa 100644 --- a/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj +++ b/src/Workspaces/Remote/Core/Microsoft.CodeAnalysis.Remote.Workspaces.csproj @@ -63,6 +63,7 @@ + diff --git a/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs b/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs index 9907fee627cde..86d25a5d2b7a0 100644 --- a/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs +++ b/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs @@ -4,19 +4,13 @@ using System; -namespace Microsoft.CodeAnalysis.Remote -{ - [Flags] - internal enum RemoteProcessConfiguration - { - /// - /// Remote host runs on .NET 6+. - /// - Core = 1, +namespace Microsoft.CodeAnalysis.Remote; - /// - /// Remote host uses server GC. - /// - ServerGC = 1 << 1, - } +[Flags] +internal enum RemoteProcessConfiguration +{ + /// + /// Remote host uses server GC. + /// + ServerGC = 1, } diff --git a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx index eb93919aea4f5..fd418d75e0cfb 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx +++ b/src/Workspaces/Remote/Core/RemoteWorkspacesResources.resx @@ -228,4 +228,7 @@ Source generation + + Semantic search + \ No newline at end of file diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index c375fee080ccb..f635780e0b524 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -26,6 +26,7 @@ using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.NavigationBar; using Microsoft.CodeAnalysis.Rename; +using Microsoft.CodeAnalysis.SemanticSearch; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SourceGeneration; using Microsoft.CodeAnalysis.StackTraceExplorer; @@ -84,10 +85,11 @@ internal sealed class ServiceDescriptors (typeof(IRemoteStackTraceExplorerService), null), (typeof(IRemoteUnitTestingSearchService), null), (typeof(IRemoteSourceGenerationService), null), + (typeof(IRemoteSemanticSearchService), typeof(IRemoteSemanticSearchService.ICallback)), }); internal readonly RemoteSerializationOptions Options; - private readonly ImmutableDictionary _descriptors; + private readonly ImmutableDictionary _descriptors; private readonly string _componentName; private readonly Func _featureDisplayNameProvider; @@ -113,17 +115,15 @@ internal static string GetSimpleName(Type serviceInterface) return interfaceName.Substring(InterfaceNamePrefix.Length, interfaceName.Length - InterfaceNamePrefix.Length - InterfaceNameSuffix.Length); } - private (ServiceDescriptor, ServiceDescriptor, ServiceDescriptor, ServiceDescriptor) CreateDescriptors(Type serviceInterface, Type? callbackInterface) + private (ServiceDescriptor descriptorCoreClr64, ServiceDescriptor descriptorCoreClr64ServerGC) CreateDescriptors(Type serviceInterface, Type? callbackInterface) { Contract.ThrowIfFalse(callbackInterface == null || callbackInterface.IsInterface); var simpleName = GetSimpleName(serviceInterface); - var descriptor64 = ServiceDescriptor.CreateRemoteServiceDescriptor(_componentName, simpleName, Suffix64, Options, _featureDisplayNameProvider, callbackInterface); - var descriptor64ServerGC = ServiceDescriptor.CreateRemoteServiceDescriptor(_componentName, simpleName, Suffix64 + SuffixServerGC, Options, _featureDisplayNameProvider, callbackInterface); var descriptorCoreClr64 = ServiceDescriptor.CreateRemoteServiceDescriptor(_componentName, simpleName, SuffixCoreClr + Suffix64, Options, _featureDisplayNameProvider, callbackInterface); var descriptorCoreClr64ServerGC = ServiceDescriptor.CreateRemoteServiceDescriptor(_componentName, simpleName, SuffixCoreClr + Suffix64 + SuffixServerGC, Options, _featureDisplayNameProvider, callbackInterface); - return (descriptor64, descriptor64ServerGC, descriptorCoreClr64, descriptorCoreClr64ServerGC); + return (descriptorCoreClr64, descriptorCoreClr64ServerGC); } public static bool IsCurrentProcessRunningOnCoreClr() @@ -131,17 +131,20 @@ public static bool IsCurrentProcessRunningOnCoreClr() !RuntimeInformation.FrameworkDescription.StartsWith(".NET Native"); public ServiceDescriptor GetServiceDescriptorForServiceFactory(Type serviceType) - => GetServiceDescriptor(serviceType, RemoteProcessConfiguration.ServerGC | (IsCurrentProcessRunningOnCoreClr() ? RemoteProcessConfiguration.Core : 0)); + => GetServiceDescriptor(serviceType, RemoteProcessConfiguration.ServerGC); public ServiceDescriptor GetServiceDescriptor(Type serviceType, RemoteProcessConfiguration configuration) { - var (descriptor64, descriptor64ServerGC, descriptorCoreClr64, descriptorCoreClr64ServerGC) = _descriptors[serviceType]; - return (configuration & (RemoteProcessConfiguration.Core | RemoteProcessConfiguration.ServerGC)) switch + if (!_descriptors.TryGetValue(serviceType, out var descriptor)) { - 0 => descriptor64, - RemoteProcessConfiguration.Core => descriptorCoreClr64, - RemoteProcessConfiguration.ServerGC => descriptor64ServerGC, - RemoteProcessConfiguration.Core | RemoteProcessConfiguration.ServerGC => descriptorCoreClr64ServerGC, + throw ExceptionUtilities.UnexpectedValue(serviceType); + } + + var (descriptorCoreClr64, descriptorCoreClr64ServerGC) = descriptor; + return (configuration & RemoteProcessConfiguration.ServerGC) switch + { + 0 => descriptorCoreClr64, + RemoteProcessConfiguration.ServerGC => descriptorCoreClr64ServerGC, _ => throw ExceptionUtilities.Unreachable() }; } @@ -162,7 +165,7 @@ internal readonly struct TestAccessor internal TestAccessor(ServiceDescriptors serviceDescriptors) => _serviceDescriptors = serviceDescriptors; - public ImmutableDictionary Descriptors + public ImmutableDictionary Descriptors => _serviceDescriptors._descriptors; } } diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf index 4bfac139d4ece..3aa7be77cf0d7 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.cs.xlf @@ -127,6 +127,11 @@ Sémantická klasifikace + + Semantic search + Semantic search + + Asset provider Poskytovatel prostředků diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf index 43f902907eec6..5ae865fc5b8fd 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.de.xlf @@ -127,6 +127,11 @@ Semantische Klassifizierung + + Semantic search + Semantic search + + Asset provider Objektanbieter diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf index 8ffd6ab4b1a91..c1929b307024e 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.es.xlf @@ -127,6 +127,11 @@ Clasificación semántica + + Semantic search + Semantic search + + Asset provider Proveedor de recursos diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf index 9b75da6666dd9..9dcc51bd94980 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.fr.xlf @@ -127,6 +127,11 @@ Classification sémantique + + Semantic search + Semantic search + + Asset provider Fournisseur de ressources diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf index 4c22af8b51f15..5b2dcec1e03dd 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.it.xlf @@ -127,6 +127,11 @@ Classificazione semantica + + Semantic search + Semantic search + + Asset provider Provider di asset diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf index 759c13e1fd037..a848834c20024 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ja.xlf @@ -127,6 +127,11 @@ セマンティック分類 + + Semantic search + Semantic search + + Asset provider 資産プロバイダー diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf index f98b7cce44952..6f466ead35540 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ko.xlf @@ -127,6 +127,11 @@ 의미 체계 분류 + + Semantic search + Semantic search + + Asset provider 자산 공급자 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf index cfe2fc8c9fcdf..a7f318568feab 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pl.xlf @@ -127,6 +127,11 @@ Klasyfikacja semantyczna + + Semantic search + Semantic search + + Asset provider Dostawca elementów zawartości diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf index a6dab4774b523..aec02cfff9b61 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.pt-BR.xlf @@ -127,6 +127,11 @@ Classificação semântica + + Semantic search + Semantic search + + Asset provider Provedor de ativos diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf index caedc701ca520..016f14ba153aa 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.ru.xlf @@ -127,6 +127,11 @@ Семантическая классификация + + Semantic search + Semantic search + + Asset provider Поставщик ресурсов diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf index 21895d278ba16..38a8a3610fa45 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.tr.xlf @@ -127,6 +127,11 @@ Anlamsal sınıflandırma + + Semantic search + Semantic search + + Asset provider Varlık sağlayıcısı diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf index c40948f2ced01..89f3b235ad703 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hans.xlf @@ -127,6 +127,11 @@ 语义分类 + + Semantic search + Semantic search + + Asset provider 资产提供商 diff --git a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf index 01cff839e5be5..fe5c28929bb15 100644 --- a/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Remote/Core/xlf/RemoteWorkspacesResources.zh-Hant.xlf @@ -127,6 +127,11 @@ 語意分類 + + Semantic search + Semantic search + + Asset provider 資產提供者 diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index 1e9954a80bed1..ee8f0ed78198d 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -58,13 +58,14 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum, Ca // if needed again later. solution = solution.WithoutFrozenSourceGeneratedDocuments(); - var oldSolutionCompilationChecksums = await solution.CompilationState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var newSolutionCompilationChecksums = await _assetProvider.GetAssetAsync( assetHint: AssetHint.None, newSolutionChecksum, cancellationToken).ConfigureAwait(false); - var oldSolutionChecksums = await solution.CompilationState.SolutionState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var newSolutionChecksums = await _assetProvider.GetAssetAsync( assetHint: AssetHint.None, newSolutionCompilationChecksums.SolutionState, cancellationToken).ConfigureAwait(false); + var oldSolutionCompilationChecksums = await solution.CompilationState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var oldSolutionChecksums = await solution.CompilationState.SolutionState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + if (oldSolutionChecksums.Attributes != newSolutionChecksums.Attributes) { var newSolutionInfo = await _assetProvider.GetAssetAsync( @@ -87,10 +88,11 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum, Ca } if (newSolutionCompilationChecksums.FrozenSourceGeneratedDocumentIdentities.HasValue && - newSolutionCompilationChecksums.FrozenSourceGeneratedDocuments.HasValue) + newSolutionCompilationChecksums.FrozenSourceGeneratedDocuments.HasValue && + !newSolutionCompilationChecksums.FrozenSourceGeneratedDocumentGenerationDateTimes.IsDefault) { var count = newSolutionCompilationChecksums.FrozenSourceGeneratedDocumentIdentities.Value.Count; - var _ = ArrayBuilder<(SourceGeneratedDocumentIdentity, SourceText)>.GetInstance(count, out var frozenDocuments); + var _ = ArrayBuilder<(SourceGeneratedDocumentIdentity identity, DateTime generationDateTime, SourceText text)>.GetInstance(count, out var frozenDocuments); for (var i = 0; i < count; i++) { @@ -102,8 +104,9 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum, Ca var serializableSourceText = await _assetProvider.GetAssetAsync(assetHint: newSolutionCompilationChecksums.FrozenSourceGeneratedDocuments.Value.Ids[i], documentStateChecksums.Text, cancellationToken).ConfigureAwait(false); + var generationDateTime = newSolutionCompilationChecksums.FrozenSourceGeneratedDocumentGenerationDateTimes[i]; var text = await serializableSourceText.GetTextAsync(cancellationToken).ConfigureAwait(false); - frozenDocuments.Add((identity, text)); + frozenDocuments.Add((identity, generationDateTime, text)); } solution = solution.WithFrozenSourceGeneratedDocuments(frozenDocuments.ToImmutable()); @@ -129,7 +132,6 @@ private async Task UpdateProjectsAsync( using var _1 = PooledDictionary.GetInstance(out var oldProjectIdToChecksum); using var _2 = PooledDictionary.GetInstance(out var newProjectIdToChecksum); - using var _3 = PooledHashSet.GetInstance(out var allProjectIds); foreach (var (oldChecksum, projectId) in oldSolutionChecksums.Projects) oldProjectIdToChecksum.Add(projectId, oldChecksum); @@ -137,8 +139,17 @@ private async Task UpdateProjectsAsync( foreach (var (newChecksum, projectId) in newSolutionChecksums.Projects) newProjectIdToChecksum.Add(projectId, newChecksum); - allProjectIds.AddRange(oldSolutionChecksums.Projects.Ids); - allProjectIds.AddRange(newSolutionChecksums.Projects.Ids); + // remove projects that are the same on both sides. We can just iterate over one of the maps as, + // definitionally, for the project to be on both sides, it will be contained in both. + foreach (var (oldChecksum, projectId) in oldSolutionChecksums.Projects) + { + if (newProjectIdToChecksum.TryGetValue(projectId, out var newChecksum) && + oldChecksum == newChecksum) + { + oldProjectIdToChecksum.Remove(projectId); + newProjectIdToChecksum.Remove(projectId); + } + } // If there are old projects that are now missing on the new side, and this is a projectConeSync, then // exclude them from the old side as well. This way we only consider projects actually added or @@ -161,27 +172,16 @@ private async Task UpdateProjectsAsync( Contract.ThrowIfFalse(oldProjectIdToChecksum.Keys.All(newProjectIdToChecksum.Keys.Contains)); } - // remove projects that are the same on both sides. - foreach (var projectId in allProjectIds) - { - if (oldProjectIdToChecksum.TryGetValue(projectId, out var oldChecksum) && - newProjectIdToChecksum.TryGetValue(projectId, out var newChecksum) && - oldChecksum == newChecksum) - { - oldProjectIdToChecksum.Remove(projectId); - newProjectIdToChecksum.Remove(projectId); - } - } - - using var _4 = PooledDictionary.GetInstance(out var oldProjectIdToStateChecksums); - using var _5 = PooledDictionary.GetInstance(out var newProjectIdToStateChecksums); + using var _3 = PooledDictionary.GetInstance(out var oldProjectIdToStateChecksums); + using var _4 = PooledDictionary.GetInstance(out var newProjectIdToStateChecksums); + // Now, find the full state checksums for all the old projects foreach (var (projectId, oldChecksum) in oldProjectIdToChecksum) { - var oldProjectState = solutionState.GetRequiredProjectState(projectId); - // this should be cheap since we already computed oldSolutionChecksums (which calls into this). - var oldProjectStateChecksums = await oldProjectState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var oldProjectStateChecksums = await solutionState + .GetRequiredProjectState(projectId) + .GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); Contract.ThrowIfTrue(oldProjectStateChecksums.ProjectId != projectId); Contract.ThrowIfTrue(oldChecksum != oldProjectStateChecksums.Checksum); @@ -190,7 +190,7 @@ private async Task UpdateProjectsAsync( // sync over the *info* about all the added/changed projects. We'll want the info so we can determine // what actually changed. - using var _6 = PooledHashSet.GetInstance(out var newChecksumsToSync); + using var _5 = PooledHashSet.GetInstance(out var newChecksumsToSync); newChecksumsToSync.AddRange(newProjectIdToChecksum.Values); var newProjectStateChecksums = await _assetProvider.GetAssetsAsync( @@ -436,21 +436,56 @@ private async Task UpdateDocumentsAsync( Func, Solution> removeDocuments, CancellationToken cancellationToken) where TDocumentState : TextDocumentState { - using var _1 = PooledHashSet.GetInstance(out var olds); - using var _2 = PooledHashSet.GetInstance(out var news); + using var _1 = PooledDictionary.GetInstance(out var oldDocumentIdToChecksum); + using var _2 = PooledDictionary.GetInstance(out var newDocumentIdToChecksum); - olds.AddRange(oldChecksums.Checksums.Children); - news.AddRange(newChecksums.Checksums.Children); + foreach (var (oldChecksum, documentId) in oldChecksums) + oldDocumentIdToChecksum.Add(documentId, oldChecksum); - // remove documents that exist in both side - olds.ExceptWith(newChecksums.Checksums); - news.ExceptWith(oldChecksums.Checksums); + foreach (var (newChecksum, documentId) in newChecksums) + newDocumentIdToChecksum.Add(documentId, newChecksum); + + // remove documents that are the same on both sides. We can just iterate over one of the maps as, + // definitionally, for the project to be on both sides, it will be contained in both. + foreach (var (oldChecksum, documentId) in oldChecksums) + { + if (newDocumentIdToChecksum.TryGetValue(documentId, out var newChecksum) && + oldChecksum == newChecksum) + { + oldDocumentIdToChecksum.Remove(documentId); + newDocumentIdToChecksum.Remove(documentId); + } + } using var _3 = PooledDictionary.GetInstance(out var oldDocumentIdToStateChecksums); using var _4 = PooledDictionary.GetInstance(out var newDocumentIdToStateChecksums); - await PopulateOldDocumentMapAsync().ConfigureAwait(false); - await PopulateNewDocumentMapAsync(this).ConfigureAwait(false); + // Now, find the full state checksums for all the old documents + foreach (var (documentId, oldChecksum) in oldDocumentIdToChecksum) + { + // this should be cheap since we already computed oldSolutionChecksums (which calls into this). + var oldDocumentStateChecksums = await existingTextDocumentStates + .GetRequiredState(documentId) + .GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + Contract.ThrowIfTrue(oldDocumentStateChecksums.DocumentId != documentId); + Contract.ThrowIfTrue(oldDocumentStateChecksums.Checksum != oldChecksum); + + oldDocumentIdToStateChecksums.Add(documentId, oldDocumentStateChecksums); + } + + // sync over the *info* about all the added/changed documents. We'll want the info so we can determine + // what actually changed. + using var _5 = PooledHashSet.GetInstance(out var newChecksumsToSync); + newChecksumsToSync.AddRange(newDocumentIdToChecksum.Values); + + var documentStateChecksums = await _assetProvider.GetAssetsAsync( + assetHint: project.Id, newChecksumsToSync, cancellationToken).ConfigureAwait(false); + + foreach (var (checksum, documentStateChecksum) in documentStateChecksums) + { + Contract.ThrowIfTrue(checksum != documentStateChecksum.Checksum); + newDocumentIdToStateChecksums.Add(documentStateChecksum.DocumentId, documentStateChecksum); + } // If more than two documents changed during a single update, perform a bulk synchronization on the // project to avoid large numbers of small synchronization calls during document updates. @@ -460,6 +495,17 @@ private async Task UpdateDocumentsAsync( await _assetProvider.SynchronizeProjectAssetsAsync(projectChecksums, cancellationToken).ConfigureAwait(false); } + return await UpdateDocumentsAsync(project, addDocuments, removeDocuments, oldDocumentIdToStateChecksums, newDocumentIdToStateChecksums, cancellationToken).ConfigureAwait(false); + } + + private async Task UpdateDocumentsAsync( + Project project, + Func, Solution> addDocuments, + Func, Solution> removeDocuments, + Dictionary oldDocumentIdToStateChecksums, + Dictionary newDocumentIdToStateChecksums, + CancellationToken cancellationToken) + { // added document ImmutableArray.Builder? lazyDocumentsToAdd = null; foreach (var (documentId, newDocumentChecksums) in newDocumentIdToStateChecksums) @@ -480,22 +526,6 @@ private async Task UpdateDocumentsAsync( project = addDocuments(project.Solution, lazyDocumentsToAdd.ToImmutable()).GetProject(project.Id)!; } - // changed document - foreach (var (documentId, newDocumentChecksums) in newDocumentIdToStateChecksums) - { - if (!oldDocumentIdToStateChecksums.TryGetValue(documentId, out var oldDocumentChecksums)) - { - continue; - } - - Contract.ThrowIfTrue(oldDocumentChecksums.Checksum == newDocumentChecksums.Checksum); - - var document = project.GetDocument(documentId) ?? project.GetAdditionalDocument(documentId) ?? project.GetAnalyzerConfigDocument(documentId); - Contract.ThrowIfNull(document); - - project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, cancellationToken).ConfigureAwait(false); - } - // removed document ImmutableArray.Builder? lazyDocumentsToRemove = null; foreach (var (documentId, _) in oldDocumentIdToStateChecksums) @@ -513,26 +543,23 @@ private async Task UpdateDocumentsAsync( project = removeDocuments(project.Solution, lazyDocumentsToRemove.ToImmutable()).GetProject(project.Id)!; } - return project; - - async Task PopulateOldDocumentMapAsync() + // changed document + foreach (var (documentId, newDocumentChecksums) in newDocumentIdToStateChecksums) { - foreach (var (_, state) in existingTextDocumentStates.States) + if (!oldDocumentIdToStateChecksums.TryGetValue(documentId, out var oldDocumentChecksums)) { - var documentChecksums = await state.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - if (olds.Contains(documentChecksums.Checksum)) - oldDocumentIdToStateChecksums.Add(state.Id, documentChecksums); + continue; } - } - async Task PopulateNewDocumentMapAsync(SolutionCreator @this) - { - var documentStateChecksums = await @this._assetProvider.GetAssetsAsync( - assetHint: project.Id, news, cancellationToken).ConfigureAwait(false); + Contract.ThrowIfTrue(oldDocumentChecksums.Checksum == newDocumentChecksums.Checksum); - foreach (var (_, documentStateChecksum) in documentStateChecksums) - newDocumentIdToStateChecksums.Add(documentStateChecksum.DocumentId, documentStateChecksum); + var document = project.GetDocument(documentId) ?? project.GetAdditionalDocument(documentId) ?? project.GetAnalyzerConfigDocument(documentId); + Contract.ThrowIfNull(document); + + project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, cancellationToken).ConfigureAwait(false); } + + return project; } private async Task UpdateDocumentAsync(TextDocument document, DocumentStateChecksums oldDocumentChecksums, DocumentStateChecksums newDocumentChecksums, CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index 0471dfcde019e..3ac0f34a4bf9e 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -25,12 +25,6 @@ internal sealed partial class RemoteWorkspace : Workspace /// private readonly SemaphoreSlim _gate = new(initialCount: 1); - /// - /// Used to make sure we never move remote workspace backward. this version is the WorkspaceVersion of primary - /// solution in client (VS) we are currently caching. - /// - private int _currentRemoteWorkspaceVersion = -1; - // internal for testing purposes. internal RemoteWorkspace(HostServices hostServices) : base(hostServices, WorkspaceKind.RemoteWorkspace) @@ -52,7 +46,7 @@ public AssetProvider CreateAssetProvider(Checksum solutionChecksum, SolutionAsse /// them to be pre-populated for feature requests that come in soon after this call completes. /// public async Task UpdatePrimaryBranchSolutionAsync( - AssetProvider assetProvider, Checksum solutionChecksum, int workspaceVersion, CancellationToken cancellationToken) + AssetProvider assetProvider, Checksum solutionChecksum, CancellationToken cancellationToken) { // See if the current snapshot we're pointing at is the same one the host wants us to sync to. If so, we // don't need to do anything. @@ -65,7 +59,6 @@ public async Task UpdatePrimaryBranchSolutionAsync( await RunWithSolutionAsync( assetProvider, solutionChecksum, - workspaceVersion, updatePrimaryBranch: true, implementation: static _ => ValueTaskFactory.FromResult(false), cancellationToken).ConfigureAwait(false); @@ -90,13 +83,12 @@ await RunWithSolutionAsync( Func> implementation, CancellationToken cancellationToken) { - return RunWithSolutionAsync(assetProvider, solutionChecksum, workspaceVersion: -1, updatePrimaryBranch: false, implementation, cancellationToken); + return RunWithSolutionAsync(assetProvider, solutionChecksum, updatePrimaryBranch: false, implementation, cancellationToken); } private async ValueTask<(Solution solution, T result)> RunWithSolutionAsync( AssetProvider assetProvider, Checksum solutionChecksum, - int workspaceVersion, bool updatePrimaryBranch, Func> implementation, CancellationToken cancellationToken) @@ -131,7 +123,7 @@ await RunWithSolutionAsync( try { inFlightSolution = GetOrCreateSolutionAndAddInFlightCount_NoLock( - assetProvider, solutionChecksum, workspaceVersion, updatePrimaryBranch); + assetProvider, solutionChecksum, updatePrimaryBranch); solutionTask = inFlightSolution.PreferredSolutionTask_NoLock; // We must have at least 1 for the in-flight-count (representing this current in-flight call). @@ -268,34 +260,15 @@ private Solution CreateSolutionFromInfo(SolutionInfo solutionInfo) } /// - /// Attempts to update this workspace with the given . If this succeeds, will be returned in the tuple result as well as the actual solution that the workspace is - /// updated to point at. If we cannot update this workspace, then will be returned, - /// along with the solution passed in. The only time the solution can not be updated is if it would move backwards. + /// Updates this workspace with the given . The solution returned is the actual + /// one the workspace now points to. /// - private async Task TryUpdateWorkspaceCurrentSolutionAsync( - int workspaceVersion, - Solution newSolution, - CancellationToken cancellationToken) - { - var (solution, _) = await TryUpdateWorkspaceCurrentSolutionWorkerAsync(workspaceVersion, newSolution, cancellationToken).ConfigureAwait(false); - return solution; - } - - private async ValueTask<(Solution solution, bool updated)> TryUpdateWorkspaceCurrentSolutionWorkerAsync( - int workspaceVersion, + private async Task UpdateWorkspaceCurrentSolutionAsync( Solution newSolution, CancellationToken cancellationToken) { using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - // Never move workspace backward - if (workspaceVersion <= _currentRemoteWorkspaceVersion) - return (newSolution, updated: false); - - _currentRemoteWorkspaceVersion = workspaceVersion; - // if either solution id or file path changed, then we consider it as new solution. Otherwise, // update the current solution in place. @@ -316,7 +289,7 @@ private async Task TryUpdateWorkspaceCurrentSolutionAsync( } }); - return (newSolution, updated: true); + return newSolution; } static bool IsAddingSolution(Solution oldSolution, Solution newSolution) @@ -338,18 +311,17 @@ public TestAccessor(RemoteWorkspace remoteWorkspace) public Solution CreateSolutionFromInfo(SolutionInfo solutionInfo) => _remoteWorkspace.CreateSolutionFromInfo(solutionInfo); - public ValueTask<(Solution solution, bool updated)> TryUpdateWorkspaceCurrentSolutionAsync(Solution newSolution, int workspaceVersion) - => _remoteWorkspace.TryUpdateWorkspaceCurrentSolutionWorkerAsync(workspaceVersion, newSolution, CancellationToken.None); + public Task UpdateWorkspaceCurrentSolutionAsync(Solution newSolution) + => _remoteWorkspace.UpdateWorkspaceCurrentSolutionAsync(newSolution, CancellationToken.None); public async ValueTask GetSolutionAsync( AssetProvider assetProvider, Checksum solutionChecksum, bool updatePrimaryBranch, - int workspaceVersion, CancellationToken cancellationToken) { var (solution, _) = await _remoteWorkspace.RunWithSolutionAsync( - assetProvider, solutionChecksum, workspaceVersion, updatePrimaryBranch, _ => ValueTaskFactory.FromResult(false), cancellationToken).ConfigureAwait(false); + assetProvider, solutionChecksum, updatePrimaryBranch, _ => ValueTaskFactory.FromResult(false), cancellationToken).ConfigureAwait(false); return solution; } } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace_SolutionCaching.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace_SolutionCaching.cs index d6c82aee29f98..a5890aba70d40 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace_SolutionCaching.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace_SolutionCaching.cs @@ -4,12 +4,10 @@ using System; using System.Collections.Generic; -using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { - internal sealed partial class RemoteWorkspace { /// @@ -40,7 +38,6 @@ internal sealed partial class RemoteWorkspace private InFlightSolution GetOrCreateSolutionAndAddInFlightCount_NoLock( AssetProvider assetProvider, Checksum solutionChecksum, - int workspaceVersion, bool updatePrimaryBranch) { Contract.ThrowIfFalse(_gate.CurrentCount == 0); @@ -57,7 +54,7 @@ private InFlightSolution GetOrCreateSolutionAndAddInFlightCount_NoLock( if (updatePrimaryBranch) { solution.TryKickOffPrimaryBranchWork_NoLock((disconnectedSolution, cancellationToken) => - this.TryUpdateWorkspaceCurrentSolutionAsync(workspaceVersion, disconnectedSolution, cancellationToken)); + this.UpdateWorkspaceCurrentSolutionAsync(disconnectedSolution, cancellationToken)); } CheckCacheInvariants_NoLock(); diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index ea25f6948ee99..5d27541525254 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -30,7 +30,7 @@ public RemoteAssetSynchronizationService(in ServiceConstructionArguments argumen { } - public ValueTask SynchronizePrimaryWorkspaceAsync(Checksum solutionChecksum, int workspaceVersion, CancellationToken cancellationToken) + public ValueTask SynchronizePrimaryWorkspaceAsync(Checksum solutionChecksum, CancellationToken cancellationToken) { return RunServiceAsync(async cancellationToken => { @@ -38,7 +38,7 @@ public ValueTask SynchronizePrimaryWorkspaceAsync(Checksum solutionChecksum, int { var workspace = GetWorkspace(); var assetProvider = workspace.CreateAssetProvider(solutionChecksum, WorkspaceManager.SolutionAssetCache, SolutionAssetSource); - await workspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, workspaceVersion, cancellationToken).ConfigureAwait(false); + await workspace.UpdatePrimaryBranchSolutionAsync(assetProvider, solutionChecksum, cancellationToken).ConfigureAwait(false); } }, cancellationToken); } @@ -65,10 +65,11 @@ public ValueTask SynchronizeTextAsync(DocumentId documentId, Checksum baseTextCh // Now attempt to manually apply the edit, producing the new forked text. Store that directly in // the asset cache so that future calls to retrieve it can do so quickly, without synchronizing over // the entire document. - var newText = new SerializableSourceText(text.WithChanges(textChanges)); - var newChecksum = serializer.CreateChecksum(newText, cancellationToken); + var newText = text.WithChanges(textChanges); + var newSerializableText = new SerializableSourceText(newText, newText.GetContentHash()); + var newChecksum = serializer.CreateChecksum(newSerializableText, cancellationToken); - WorkspaceManager.SolutionAssetCache.GetOrAdd(newChecksum, newText); + WorkspaceManager.SolutionAssetCache.GetOrAdd(newChecksum, newSerializableText); } return; diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs new file mode 100644 index 0000000000000..8446af1308920 --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.SemanticSearch; + +namespace Microsoft.CodeAnalysis.Remote; + +internal sealed class RemoteSemanticSearchService( + in BrokeredServiceBase.ServiceConstructionArguments arguments, + RemoteCallback callback) + : BrokeredServiceBase(arguments), IRemoteSemanticSearchService +{ + internal sealed class Factory : FactoryBase + { + protected override IRemoteSemanticSearchService CreateService(in ServiceConstructionArguments arguments, RemoteCallback callback) + => new RemoteSemanticSearchService(arguments, callback); + } + + private sealed class ClientCallbacks( + RemoteCallback callback, + RemoteServiceCallbackId callbackId) : ISemanticSearchResultsObserver, OptionsProvider + { + public ValueTask GetOptionsAsync(CodeAnalysis.Host.LanguageServices languageServices, CancellationToken cancellationToken) + => callback.InvokeAsync((callback, cancellationToken) => callback.GetClassificationOptionsAsync(callbackId, languageServices.Language, cancellationToken), cancellationToken); + + public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) + => callback.InvokeAsync((callback, cancellationToken) => callback.AddItemsAsync(callbackId, itemCount, cancellationToken), cancellationToken); + + public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) + => callback.InvokeAsync((callback, cancellationToken) => callback.ItemsCompletedAsync(callbackId, itemCount, cancellationToken), cancellationToken); + + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken) + { + var dehydratedDefinition = SerializableDefinitionItem.Dehydrate(id: 0, definition); + return callback.InvokeAsync((callback, cancellationToken) => callback.OnDefinitionFoundAsync(callbackId, dehydratedDefinition, cancellationToken), cancellationToken); + } + + public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) + => callback.InvokeAsync((callback, cancellationToken) => callback.OnUserCodeExceptionAsync(callbackId, exception, cancellationToken), cancellationToken); + + public ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) + => callback.InvokeAsync((callback, cancellationToken) => callback.OnCompilationFailureAsync(callbackId, errors, cancellationToken), cancellationToken); + } + + /// + /// Remote API. + /// + public ValueTask ExecuteQueryAsync( + Checksum solutionChecksum, + RemoteServiceCallbackId callbackId, + string language, + string query, + string referenceAssembliesDir, + CancellationToken cancellationToken) + { + return RunServiceAsync(solutionChecksum, async solution => + { + var service = solution.Services.GetLanguageServices(language).GetService(); + if (service == null) + { + return new ExecuteQueryResult(FeaturesResources.Semantic_search_only_supported_on_net_core); + } + + var clientCallbacks = new ClientCallbacks(callback, callbackId); + + return await service.ExecuteQueryAsync(solution, query, referenceAssembliesDir, clientCallbacks, clientCallbacks, TraceLogger, cancellationToken).ConfigureAwait(false); + }, cancellationToken); + } +} diff --git a/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs index 05b74c0a06f1d..6cee072584d86 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.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.Threading; using System.Threading.Tasks; @@ -21,7 +22,7 @@ protected override IRemoteSourceGenerationService CreateService(in ServiceConstr => new RemoteSourceGenerationService(arguments); } - public ValueTask> GetSourceGenerationInfoAsync( + public ValueTask> GetSourceGenerationInfoAsync( Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken) { return RunServiceAsync(solutionChecksum, async solution => @@ -29,12 +30,12 @@ protected override IRemoteSourceGenerationService CreateService(in ServiceConstr var project = solution.GetRequiredProject(projectId); var documentStates = await solution.CompilationState.GetSourceGeneratedDocumentStatesAsync(project.State, cancellationToken).ConfigureAwait(false); - using var _ = ArrayBuilder<(SourceGeneratedDocumentIdentity documentIdentity, SourceGeneratedDocumentContentIdentity contentIdentity)>.GetInstance(documentStates.Ids.Count, out var result); + using var _ = ArrayBuilder<(SourceGeneratedDocumentIdentity documentIdentity, SourceGeneratedDocumentContentIdentity contentIdentity, DateTime generationDateTime)>.GetInstance(documentStates.Ids.Count, out var result); foreach (var (id, state) in documentStates.States) { Contract.ThrowIfFalse(id.IsSourceGenerated); - result.Add((state.Identity, state.GetContentIdentity())); + result.Add((state.Identity, state.GetContentIdentity(), state.GenerationDateTime)); } return result.ToImmutableAndClear(); @@ -62,4 +63,12 @@ public ValueTask> GetContentsAsync( return result.ToImmutableAndClear(); }, cancellationToken); } + + public ValueTask HasGeneratorsAsync(Checksum solutionChecksum, ProjectId projectId, CancellationToken cancellationToken) + { + return RunServiceAsync(solutionChecksum, async solution => + { + return await solution.CompilationState.HasSourceGeneratorsAsync(projectId, cancellationToken).ConfigureAwait(false); + }, cancellationToken); + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index b03846c9f11a5..792289b5df9da 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -1681,14 +1681,23 @@ public void GetPartsOfNamedMemberInitializer(SyntaxNode node, out SyntaxNode ide expression = assignment.Right; } - public void GetPartsOfObjectCreationExpression(SyntaxNode node, out SyntaxNode type, out SyntaxNode? argumentList, out SyntaxNode? initializer) + public void GetPartsOfObjectCreationExpression(SyntaxNode node, out SyntaxToken keyword, out SyntaxNode type, out SyntaxNode? argumentList, out SyntaxNode? initializer) { var objectCreationExpression = (ObjectCreationExpressionSyntax)node; + keyword = objectCreationExpression.NewKeyword; type = objectCreationExpression.Type; argumentList = objectCreationExpression.ArgumentList; initializer = objectCreationExpression.Initializer; } + public void GetPartsOfImplicitObjectCreationExpression(SyntaxNode node, out SyntaxToken keyword, out SyntaxNode argumentList, out SyntaxNode? initializer) + { + var implicitObjectCreationExpression = (ImplicitObjectCreationExpressionSyntax)node; + keyword = implicitObjectCreationExpression.NewKeyword; + argumentList = implicitObjectCreationExpression.ArgumentList; + initializer = implicitObjectCreationExpression.Initializer; + } + public void GetPartsOfParameter(SyntaxNode node, out SyntaxToken identifier, out SyntaxNode? @default) { var parameter = (ParameterSyntax)node; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs index de81fa58d62d6..3c8f9c36452cc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs @@ -769,7 +769,7 @@ bool IsSupportedConstructWithNullType() return true; } - // Similar to above, it's fine for a collection expression to have a a 'null' direct type (as long as a + // Similar to above, it's fine for a collection expression to have a 'null' direct type (as long as a // target-typed collection-expression conversion happened). Note: unlike above, we don't have to check // a language version since collection expressions always supported collection-expression-conversions. if (newExpression.IsKind(SyntaxKind.CollectionExpression) && @@ -778,6 +778,16 @@ bool IsSupportedConstructWithNullType() return true; } + // Similar to above, it's fine for a tuple expression to have a 'null' direct type (as long as a + // target-typed tuple-expression conversion happened). Note: unlike above, we don't have to check + // a language version since tuple expressions always supported tuple-expression-conversions. + if (newExpression.IsKind(SyntaxKind.TupleExpression) && + this.SpeculativeSemanticModel.GetConversion(newExpression).IsTupleLiteralConversion && + SymbolsAreCompatible(originalTypeInfo.Type, newTypeInfo.ConvertedType)) + { + return true; + } + return false; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index 77d2d04e8bddc..e9a169c4ce9e8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -622,6 +622,9 @@ internal enum FunctionId RemoteWorkspace_SolutionCachingStatistics = 750, + SemanticSearch_QueryExecution = 760, + // 800-850 for Copilot performance logging. Copilot_Suggestion_Dismissed = 800, + Copilot_Rename = 851 } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index 969b8f3878681..3fc36df6dda1b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -528,7 +528,8 @@ void GetPartsOfTupleExpression(SyntaxNode node, void GetPartsOfIsPatternExpression(SyntaxNode node, out SyntaxNode left, out SyntaxToken isToken, out SyntaxNode right); void GetPartsOfMemberAccessExpression(SyntaxNode node, out SyntaxNode expression, out SyntaxToken operatorToken, out SyntaxNode name); void GetPartsOfNamedMemberInitializer(SyntaxNode node, out SyntaxNode name, out SyntaxNode expression); - void GetPartsOfObjectCreationExpression(SyntaxNode node, out SyntaxNode type, out SyntaxNode? argumentList, out SyntaxNode? initializer); + void GetPartsOfObjectCreationExpression(SyntaxNode node, out SyntaxToken keyword, out SyntaxNode type, out SyntaxNode? argumentList, out SyntaxNode? initializer); + void GetPartsOfImplicitObjectCreationExpression(SyntaxNode node, out SyntaxToken keyword, out SyntaxNode argumentList, out SyntaxNode? initializer); void GetPartsOfParameter(SyntaxNode node, out SyntaxToken identifier, out SyntaxNode? @default); void GetPartsOfParenthesizedExpression(SyntaxNode node, out SyntaxToken openParen, out SyntaxNode expression, out SyntaxToken closeParen); void GetPartsOfPrefixUnaryExpression(SyntaxNode node, out SyntaxToken operatorToken, out SyntaxNode operand); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 29deeaeb60fc6..3f2af4340e6e9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -595,7 +595,7 @@ public static SeparatedSyntaxList GetTypeArgumentsOfGenericName(this public static SyntaxNode GetTypeOfObjectCreationExpression(this ISyntaxFacts syntaxFacts, SyntaxNode node) { - syntaxFacts.GetPartsOfObjectCreationExpression(node, out var type, out _, out _); + syntaxFacts.GetPartsOfObjectCreationExpression(node, out _, out var type, out _, out _); return type; } @@ -648,7 +648,7 @@ public static bool IsTypeOfObjectCreationExpression(this ISyntaxFacts syntaxFact if (!syntaxFacts.IsObjectCreationExpression(parent)) return false; - syntaxFacts.GetPartsOfObjectCreationExpression(parent, out var type, out _, out _); + syntaxFacts.GetPartsOfObjectCreationExpression(parent, out _, out var type, out _, out _); return type == node; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 0bb0d7c271698..a805b26656095 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -1895,13 +1895,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService expression = namedField.Expression End Sub - Public Sub GetPartsOfObjectCreationExpression(node As SyntaxNode, ByRef type As SyntaxNode, ByRef argumentList As SyntaxNode, ByRef initializer As SyntaxNode) Implements ISyntaxFacts.GetPartsOfObjectCreationExpression + Public Sub GetPartsOfObjectCreationExpression(node As SyntaxNode, ByRef keyword As SyntaxToken, ByRef type As SyntaxNode, ByRef argumentList As SyntaxNode, ByRef initializer As SyntaxNode) Implements ISyntaxFacts.GetPartsOfObjectCreationExpression Dim objectCreationExpression = DirectCast(node, ObjectCreationExpressionSyntax) + keyword = objectCreationExpression.NewKeyword type = objectCreationExpression.Type argumentList = objectCreationExpression.ArgumentList initializer = objectCreationExpression.Initializer End Sub + Public Sub GetPartsOfImplicitObjectCreationExpression(node As SyntaxNode, ByRef keyword As SyntaxToken, ByRef argumentList As SyntaxNode, ByRef initializer As SyntaxNode) Implements ISyntaxFacts.GetPartsOfImplicitObjectCreationExpression + Throw New InvalidOperationException(DoesNotExistInVBErrorMessage) + End Sub + Public Sub GetPartsOfParameter(node As SyntaxNode, ByRef identifier As SyntaxToken, ByRef [default] As SyntaxNode) Implements ISyntaxFacts.GetPartsOfParameter Dim parameter = DirectCast(node, ParameterSyntax) identifier = parameter.Identifier.Identifier diff --git a/src/Workspaces/TestAnalyzerReference/HelloWorldGenerator.cs b/src/Workspaces/TestAnalyzerReference/HelloWorldGenerator.cs index cf5e8866d86ad..93982f590ab99 100644 --- a/src/Workspaces/TestAnalyzerReference/HelloWorldGenerator.cs +++ b/src/Workspaces/TestAnalyzerReference/HelloWorldGenerator.cs @@ -22,26 +22,26 @@ public void Initialize(GeneratorInitializationContext context) public void Execute(GeneratorExecutionContext context) { - context.AddSource(GeneratedEnglishClassName, SourceText.From(@" -/// is a simple class to fetch the classic message. -internal class " + GeneratedEnglishClassName + @" -{ - public static string GetMessage() - { - return ""Hello, World!""; - } -} -", encoding: Encoding.UTF8)); + context.AddSource(GeneratedEnglishClassName, SourceText.From($$""" + /// is a simple class to fetch the classic message. + internal class {{GeneratedEnglishClassName}} + { + public static string GetMessage() + { + return "Hello, World!"; + } + } + """, encoding: Encoding.UTF8)); - context.AddSource(GeneratedSpanishClassName, SourceText.From(@" -internal class " + GeneratedSpanishClassName + @" -{ - public static string GetMessage() - { - return ""Hola, Mundo!""; - } -} -", encoding: Encoding.UTF8)); + context.AddSource(GeneratedSpanishClassName, SourceText.From($$""" + internal class {{GeneratedSpanishClassName}} + { + public static string GetMessage() + { + return "Hola, Mundo!"; + } + } + """, encoding: Encoding.UTF8)); context.AddSource($"{GeneratedFolderName}/{GeneratedFolderClassName}", $$""" class {{GeneratedFolderClassName}} { } diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index b5eabd5569fa1..bcfa7cbc632d4 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -1675,6 +1675,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return attr.WithTarget(Nothing) End Function + Friend Overrides Function GetPrimaryConstructorParameterList(declaration As SyntaxNode) As SyntaxNode + Return Nothing + End Function + Friend Overrides Function GetTypeInheritance(declaration As SyntaxNode) As ImmutableArray(Of SyntaxNode) Dim typeDecl = TryCast(declaration, TypeBlockSyntax) If typeDecl Is Nothing Then diff --git a/src/Workspaces/VisualBasic/Portable/ObsoleteSymbol/VisualBasicObsoleteSymbolService.vb b/src/Workspaces/VisualBasic/Portable/ObsoleteSymbol/VisualBasicObsoleteSymbolService.vb new file mode 100644 index 0000000000000..f20879b1fdce4 --- /dev/null +++ b/src/Workspaces/VisualBasic/Portable/ObsoleteSymbol/VisualBasicObsoleteSymbolService.vb @@ -0,0 +1,57 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports System.Threading +Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.ObsoleteSymbol +Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.ObsoleteSymbol + + <[Shared]> + Friend Class VisualBasicObsoleteSymbolService + Inherits AbstractObsoleteSymbolService + + + + Public Sub New() + MyBase.New(SyntaxKind.DimKeyword) + End Sub + + Protected Overrides Sub ProcessDimKeyword(ByRef result As ArrayBuilder(Of TextSpan), semanticModel As SemanticModel, token As SyntaxToken, cancellationToken As CancellationToken) + Dim localDeclaration = TryCast(token.Parent, LocalDeclarationStatementSyntax) + If localDeclaration Is Nothing Then + Return + End If + + If localDeclaration.Declarators.Count <> 1 Then + Return + End If + + Dim declarator = localDeclaration.Declarators(0) + If declarator.AsClause IsNot Nothing Then + ' This is an explicitly typed variable, so no need to mark 'Dim' obsolete + Return + End If + + If declarator.Names.Count <> 1 Then + ' More than one variable is declared + Return + End If + + ' Only one variable is declared + Dim localSymbol = TryCast(semanticModel.GetDeclaredSymbol(declarator.Names(0), cancellationToken), ILocalSymbol) + If IsSymbolObsolete(localSymbol?.Type) Then + If result Is Nothing Then + result = ArrayBuilder(Of TextSpan).GetInstance() + End If + + result.Add(token.Span) + End If + End Sub + End Class +End Namespace diff --git a/src/Workspaces/VisualBasic/Portable/xlf/VBWorkspaceResources.de.xlf b/src/Workspaces/VisualBasic/Portable/xlf/VBWorkspaceResources.de.xlf index 5c1ddf495702f..b7655c60b9bec 100644 --- a/src/Workspaces/VisualBasic/Portable/xlf/VBWorkspaceResources.de.xlf +++ b/src/Workspaces/VisualBasic/Portable/xlf/VBWorkspaceResources.de.xlf @@ -69,7 +69,7 @@ The event handler to associate with the event. This may take the form of { AddressOf <eventHandler> | <delegate> | <lambdaExpression> }. - Der Ereignishandler, dem das Ereignis zugeordnet wird. Dies kann die Form { AddressOf <Ereignishandler> | <Delegat> | <Lambdaausdruck> } haben. + Der Ereignishandler, dem das Ereignis zugeordnet wird. Dies kann die Form { AddressOf <eventHandler> | <delegate> | <lambdaExpression> } haben. @@ -184,7 +184,7 @@ The event handler to disassociate from the event. This may take the form of { AddressOf <eventHandler> | <delegate> }. - Der Ereignishandler, von dem das Ereignis getrennt wird. Dies kann die Form { AddressOf <Ereignishandler> | <Delegat> } haben. + Der Ereignishandler, von dem das Ereignis getrennt wird. Dies kann die Form { AddressOf <eventHandler> | <delegate> } haben.