diff --git a/Compilers.slnf b/Compilers.slnf index 04cb2b44c4da1..fbb18d1909e48 100644 --- a/Compilers.slnf +++ b/Compilers.slnf @@ -46,6 +46,9 @@ "src\\Dependencies\\CodeAnalysis.Debugging\\Microsoft.CodeAnalysis.Debugging.shproj", "src\\Dependencies\\Collections\\Microsoft.CodeAnalysis.Collections.shproj", "src\\Dependencies\\PooledObjects\\Microsoft.CodeAnalysis.PooledObjects.shproj", + "src\\ExpressionEvaluator\\CSharp\\Source\\ExpressionCompiler\\Microsoft.CodeAnalysis.CSharp.ExpressionCompiler.csproj", + "src\\ExpressionEvaluator\\Core\\Source\\ExpressionCompiler\\Microsoft.CodeAnalysis.ExpressionCompiler.csproj", + "src\\ExpressionEvaluator\\VisualBasic\\Source\\ExpressionCompiler\\Microsoft.CodeAnalysis.VisualBasic.ExpressionCompiler.vbproj", "src\\Interactive\\csi\\csi.csproj", "src\\NuGet\\Microsoft.CodeAnalysis.Compilers.Package.csproj", "src\\NuGet\\Microsoft.CodeAnalysis.Package.csproj", diff --git a/NuGet.config b/NuGet.config index 1aafa4f960290..ee07d5df4b030 100644 --- a/NuGet.config +++ b/NuGet.config @@ -7,12 +7,15 @@ - + + + + diff --git a/Roslyn.sln b/Roslyn.sln index 643bd9aa04dfc..b246545c4df10 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -529,6 +529,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Net.Compilers.Too EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LanguageServer", "LanguageServer", "{D449D505-CC6A-4E0B-AF1B-976E2D0AE67A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.Features.UnitTests", "src\Features\CSharpTest\Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj", "{E645B517-5766-46FB-AA4A-D4D30C9E3BE6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Features.UnitTests", "src\Features\Test\Microsoft.CodeAnalysis.Features.UnitTests.csproj", "{9296F799-5DE4-4E12-A68E-AAC39B0EB90A}" +EndProject +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests", "src\Features\VisualBasicTest\Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests.vbproj", "{57B7C0AA-E14A-41F6-AD06-FB3937F66FC2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1291,6 +1297,18 @@ Global {521ADC3E-CC15-414B-9356-D87C5BCF3A24}.Debug|Any CPU.Build.0 = Debug|Any CPU {521ADC3E-CC15-414B-9356-D87C5BCF3A24}.Release|Any CPU.ActiveCfg = Release|Any CPU {521ADC3E-CC15-414B-9356-D87C5BCF3A24}.Release|Any CPU.Build.0 = Release|Any CPU + {E645B517-5766-46FB-AA4A-D4D30C9E3BE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E645B517-5766-46FB-AA4A-D4D30C9E3BE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E645B517-5766-46FB-AA4A-D4D30C9E3BE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E645B517-5766-46FB-AA4A-D4D30C9E3BE6}.Release|Any CPU.Build.0 = Release|Any CPU + {9296F799-5DE4-4E12-A68E-AAC39B0EB90A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9296F799-5DE4-4E12-A68E-AAC39B0EB90A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9296F799-5DE4-4E12-A68E-AAC39B0EB90A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9296F799-5DE4-4E12-A68E-AAC39B0EB90A}.Release|Any CPU.Build.0 = Release|Any CPU + {57B7C0AA-E14A-41F6-AD06-FB3937F66FC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57B7C0AA-E14A-41F6-AD06-FB3937F66FC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57B7C0AA-E14A-41F6-AD06-FB3937F66FC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57B7C0AA-E14A-41F6-AD06-FB3937F66FC2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1531,6 +1549,9 @@ Global {E5E0BF73-95F7-4BC3-8443-2336C4FF4297} = {8977A560-45C2-4EC2-A849-97335B382C74} {828FD0DB-9927-42AC-B6C2-D1514965D6C3} = {8977A560-45C2-4EC2-A849-97335B382C74} {521ADC3E-CC15-414B-9356-D87C5BCF3A24} = {C52D8057-43AF-40E6-A01B-6CDBB7301985} + {E645B517-5766-46FB-AA4A-D4D30C9E3BE6} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} + {9296F799-5DE4-4E12-A68E-AAC39B0EB90A} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} + {57B7C0AA-E14A-41F6-AD06-FB3937F66FC2} = {3E5FE3DB-45F7-4D83-9097-8F05D3B3AEC6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} @@ -1539,7 +1560,6 @@ Global src\Analyzers\VisualBasic\CodeFixes\VisualBasicCodeFixes.projitems*{0141285d-8f6c-42c7-baf3-3c0ccd61c716}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{0141285d-8f6c-42c7-baf3-3c0ccd61c716}*SharedItemsImports = 5 src\Compilers\CSharp\csc\CscCommandLine.projitems*{0161e25c-918a-4dc8-9648-30fdcc8e31e9}*SharedItemsImports = 5 - src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{0be66736-cdaa-4989-88b1-b3f46ebdca4a}*SharedItemsImports = 5 src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{1b6c4a1a-413b-41fb-9f85-5c09118e541b}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 @@ -1563,6 +1583,7 @@ Global src\Compilers\CSharp\csc\CscCommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 5 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{5018d049-5870-465a-889b-c742ce1e31cb}*SharedItemsImports = 5 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{54e08bf5-f819-404f-a18d-0ab9ea81ea04}*SharedItemsImports = 13 + src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{57b7c0aa-e14a-41f6-ad06-fb3937f66fc2}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{58969243-7f59-4236-93d0-c93b81f569b3}*SharedItemsImports = 13 @@ -1591,7 +1612,6 @@ Global src\Analyzers\CSharp\Analyzers\CSharpAnalyzers.projitems*{aa87bfed-089a-4096-b8d5-690bdc7d5b24}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{aa87bfed-089a-4096-b8d5-690bdc7d5b24}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5 - src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{ac2bcefb-9298-4621-ac48-1ff5e639e48d}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{ace53515-482c-4c6a-e2d2-4242a687dfee}*SharedItemsImports = 5 src\Compilers\CSharp\csc\CscCommandLine.projitems*{b021ccbc-b2af-4560-af28-ed055f0ed696}*SharedItemsImports = 13 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5 @@ -1606,6 +1626,7 @@ Global src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{dc8c78cc-b6fe-47bf-93b1-b65a1c67c08d}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{e512c6c1-f085-4ad7-b0d9-e8f1a0a2a510}*SharedItemsImports = 5 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 5 + src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{e645b517-5766-46fb-aa4a-d4d30c9e3be6}*SharedItemsImports = 5 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{e8f0baa5-7327-43d1-9a51-644e81ae55f1}*SharedItemsImports = 13 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{e919dd77-34f8-4f57-8058-4d3ff4c2b241}*SharedItemsImports = 13 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{e9dbfa41-7a9c-49be-bd36-fd71b31aa9fe}*SharedItemsImports = 13 diff --git a/azure-pipelines-pr-validation.yml b/azure-pipelines-pr-validation.yml index e53b7f62f4397..5c7a9f7dd7f4b 100644 --- a/azure-pipelines-pr-validation.yml +++ b/azure-pipelines-pr-validation.yml @@ -152,7 +152,7 @@ stages: displayName: Publish Assets inputs: filePath: 'eng\publish-assets.ps1' - arguments: '-configuration $(BuildConfiguration) -branchName "$(SourceBranchName)"' + arguments: '-configuration $(BuildConfiguration) -branchName "$(SourceBranchName)" -prValidation' condition: succeeded() # Publish OptProf generated JSON files as a build artifact. This allows for easy inspection from diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 806cac131a1a5..805b05793cfc6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -389,3 +389,8 @@ stages: - script: ./eng/build.sh --solution Roslyn.sln --restore --build --configuration Debug --prepareMachine --ci --binaryLog --runanalyzers --warnaserror /p:RoslynEnforceCodeStyle=true displayName: Build with analyzers + + - template: eng/pipelines/publish-logs.yml + parameters: + jobName: Correctness_Analyzers + configuration: Debug diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 361f44b3e96c0..13b8c37e9cf41 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -10,17 +10,18 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | +| [nameof accessing instance members](https://github.com/dotnet/csharplang/issues/4037) | main | [Merged into 17.7p1](https://github.com/dotnet/roslyn/issues/67565) | [YairHalberstadt](https://github.com/YairHalberstadt), [jjonescz](https://github.com/jjonescz) | [333fred](https://github.com/333fred), [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred) | | [Using aliases for any type](https://github.com/dotnet/csharplang/issues/4284) | [UsingAliasTypes](https://github.com/dotnet/roslyn/tree/features/UsingAliasTypes) | [Merged into 17.6.P3](https://github.com/dotnet/roslyn/issues/56323) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv) [cston](https://github.com/cston) | | | [Primary Constructors](https://github.com/dotnet/csharplang/issues/2691) | [PrimaryConstructors](https://github.com/dotnet/roslyn/tree/features/PrimaryConstructors) | [Merged into 17.6.P2](https://github.com/dotnet/roslyn/issues/65697) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | [MadsTorgersen](https://github.com/MadsTorgersen) | | [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) | | [Params Span\ + Stackalloc any array type](https://github.com/dotnet/csharplang/issues/1757) | [params-span](https://github.com/dotnet/roslyn/tree/features/params-span) | [In Progress](https://github.com/dotnet/roslyn/issues/57049) | [cston](https://github.com/cston) | TBD | [jaredpar](https://github.com/jaredpar) | -| [nameof accessing instance members](https://github.com/dotnet/roslyn/issues/40229) | main | [In Progress](https://github.com/dotnet/roslyn/pull/48754) | [YairHalberstadt ](https://github.com/YairHalberstadt) | [333fred](https://github.com/333fred), [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred) | | [Lambda default parameters](https://github.com/dotnet/csharplang/issues/6051) | [lambda-default-parameters](https://github.com/dotnet/roslyn/tree/features/lambda-default-parameters) | [Merged into 17.5p2](https://github.com/dotnet/roslyn/issues/62485) | [adamperlin](https://github.com/adamperlin), [jjonescz](https://github.com/jjonescz) | [333fred](https://github.com/333fred), [cston](https://github.com/cston) | [captainsafia](https://github.com/captainsafia) | | [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) | | [Collection Literals](https://github.com/dotnet/csharplang/issues/5354) | [CollectionLiterals](https://github.com/dotnet/roslyn/tree/features/CollectionLiterals) | [In Progress](https://github.com/dotnet/roslyn/issues/66418) | [cston](https://github.com/cston) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [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) | | [Interceptors](https://github.com/dotnet/csharplang/issues/7009) | [interceptors](https://github.com/dotnet/roslyn/tree/features/interceptors) | [In Progress](https://github.com/dotnet/roslyn/issues/67421) | [RikkiGibson](https://github.com/RikkiGibson) | [cston](https://github.com/cston), [jcouv](https://github.com/jcouv) | [RikkiGibson](https://github.com/RikkiGibson) | - +| [Inline Arrays](https://github.com/dotnet/csharplang/blob/main/proposals/inline-arrays.md) | [InlineArrays](https://github.com/dotnet/roslyn/tree/features/InlineArrays) | [In Progress](https://github.com/dotnet/roslyn/issues/67826) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | | + # C# 11.0 | Feature | Branch | State | Developer | Reviewer | LDM Champ | diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index c1b6dfbf05381..fe8201be8392c 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -1,5 +1,20 @@ - + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 72caae2cec902..b5765e26d49a1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,16 +1,16 @@ - - https://github.com/dotnet/xliff-tasks - 7e80445ee82adbf9a8e6ae601ac5e239d982afaa - - - + https://github.com/dotnet/source-build-externals - ee790b0477953dd30ea83e0e47af909a04fd0ca3 + 33edde07d61cf7606d76ada765335fb81f1cbb71 + + https://github.com/dotnet/source-build-reference-packages + 01850f2b7bff23e118d1faecb941d770c8e626f2 + + @@ -18,6 +18,20 @@ b12f035e893c34ec2c965d75f6e21b7a2667e98d + + https://github.com/dotnet/xliff-tasks + b7fb98b199d0b1b6188da4f4bf4f5accddac98d4 + + + + https://github.com/dotnet/sourcelink + 3f43bf1b2dead2cb51f20dc47f6dfd7981248820 + + + + https://github.com/dotnet/symreader + 636a10b9d0aa317736c57d95f80e1c0849450715 + https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 @@ -26,9 +40,9 @@ https://github.com/dotnet/arcade b12f035e893c34ec2c965d75f6e21b7a2667e98d - + https://github.com/dotnet/roslyn-analyzers - 5cd64de1a3319d7f15380b843352a2b963e59b57 + 19fb5cab215d2fe5d6c895d5f26c61dc1bdc0543 diff --git a/eng/Versions.props b/eng/Versions.props index 867ec06641f7c..1ceac561bd177 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -6,21 +6,21 @@ --> 4 - 6 + 7 0 - 3 + 1 $(MajorVersion).$(MinorVersion).$(PatchVersion) $(MajorVersion).$(MinorVersion).0.0 - 4.4.0-2.final + 4.5.0 - 3.3.4-beta1.22579.2 - 8.0.0-preview1.23111.1 + 3.3.5-beta1.23204.1 + 8.0.0-preview.23221.1 1.1.2-beta1.23163.2 0.1.149-beta @@ -107,8 +107,8 @@ 1.1.0-beta2-22302-02 17.0.0-beta1.21524.1 1.7.0-beta-21528-01 - 5.0.0 - 5.0.0 + 6.0.0 + 6.0.0 3.13.8 15.8.27812-alpha $(MicrosoftVisualStudioShellPackagesVersion) @@ -116,8 +116,7 @@ 12.3.300-rc.3.83 1.0.0-beta3.21075.2 2.2.101 - 2.1.2 - 5.0.0 + 5.0.0 1.0.0 1.0.0 1.0.0 @@ -243,7 +242,7 @@ 4.3.0 5.0.0 7.0.0 - 5.0.0-preview.8.20407.11 + 7.0.0 4.5.5 7.0.0 6.0.0 diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index 16518f188d560..e76daaa5ad04e 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -1,7 +1,8 @@ { "feeds": { "vssdk": "https://pkgs.dev.azure.com/azure-public/vside/_packaging/vssdk/nuget/v3/index.json", - "vs-impl": "https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-impl/nuget/v3/index.json" + "vs-impl": "https://pkgs.dev.azure.com/azure-public/vside/_packaging/vs-impl/nuget/v3/index.json", + "vs": "https://pkgs.dev.azure.com/DevDiv/_packaging/VS/nuget/v3/index.json" }, "packages": { "default": { @@ -143,9 +144,8 @@ "Shipping", "NonShipping" ], - "vsBranch": "main", + "vsBranch": "rel/d17.6", "vsMajorVersion": 17, - "insertionCreateDraftPR": false, "insertionTitlePrefix": "[d17.6P3]" }, "main": { @@ -155,18 +155,8 @@ ], "vsBranch": "main", "vsMajorVersion": 17, - "insertionCreateDraftPR": true, - "insertionTitlePrefix": "[Validation]" - }, - "main-vs-deps": { - "nugetKind": [ - "Shipping", - "NonShipping" - ], - "vsBranch": "main", - "vsMajorVersion": 17, - "insertionCreateDraftPR": true, - "insertionTitlePrefix": "[17.6P3]" + "insertionCreateDraftPR": false, + "insertionTitlePrefix": "[d17.7P1]" }, "dev/jorobich/fix-pr-val": { "nugetKind": [ diff --git a/eng/generate-compiler-code.ps1 b/eng/generate-compiler-code.ps1 index 3e31dd0a9c31d..3981e6f7afdec 100644 --- a/eng/generate-compiler-code.ps1 +++ b/eng/generate-compiler-code.ps1 @@ -22,7 +22,7 @@ function Run-LanguageCore($language, $languageSuffix, $languageDir, $syntaxProje $errorFileName = if ($language -eq "CSharp") { "ErrorCode.cs" } else { "Errors.vb" } $errorFilePath = Join-Path $languageDir "Errors\$errorFileName" $errorGeneratedFilePath = Join-Path $generatedDir "ErrorFacts.Generated.$($languageSuffix)" - $targetFramework = "net7.0" + $targetFramework = "net8.0" Create-Directory $generatedDir Create-Directory $generatedTestDir @@ -91,7 +91,7 @@ function Run-GetTextCore($generatedDir) { $syntaxTextFilePath = Join-Path $generatedDir "Syntax.xml.GetText.Generated.vb" Create-Directory $generatedDir - Run-Tool $basicSyntaxProject "`"$syntaxFilePath`" `"$syntaxTextFilePath`" /gettext" "net7.0" + Run-Tool $basicSyntaxProject "`"$syntaxFilePath`" `"$syntaxTextFilePath`" /gettext" "net8.0" } function Run-GetText() { diff --git a/eng/publish-assets.ps1 b/eng/publish-assets.ps1 index b81ee6b7ded1a..a3d2dfaa4fd61 100644 --- a/eng/publish-assets.ps1 +++ b/eng/publish-assets.ps1 @@ -13,6 +13,7 @@ Param( [string]$branchName = "", [string]$releaseName = "", [switch]$test, + [switch]$prValidation, # Credentials [string]$nugetApiKey = "" @@ -44,7 +45,7 @@ function Publish-Nuget($publishData, [string]$packageDir) { } # If the configured packageFeeds is arcade, then skip publishing here. Arcade will handle publishing packages to their feeds. - if ($packageFeeds.equals("arcade")) { + if ($packageFeeds.equals("arcade") -and -not $prValidation) { Write-Host " Skipping publishing for all packages as they will be published by arcade" continue } @@ -55,12 +56,6 @@ function Publish-Nuget($publishData, [string]$packageDir) { $packageFeeds = $publishData.packageFeeds } - # If the configured packageFeeds is arcade, then skip publishing here. Arcade will handle publishing packages to their feeds. - if ($packageFeeds.equals("arcade")) { - Write-Host " Skipping publishing for all packages as they will be published by arcade" - continue - } - # Each branch stores the name of the package to feed map it should use. # Retrieve the correct map for this particular branch. $packagesData = GetPackagesPublishData $packageFeeds @@ -87,6 +82,10 @@ function Publish-Nuget($publishData, [string]$packageDir) { $feedName = $packagesData.$nupkgWithoutVersion + if ($prValidation) { + $feedName = "vs" + } + # If the configured feed is arcade, then skip publishing here. Arcade will handle publishing to their feeds. if ($feedName.equals("arcade")) { Write-Host "Skipping publishing for $nupkg as it is published by arcade" diff --git a/eng/targets/Imports.BeforeArcade.targets b/eng/targets/Imports.BeforeArcade.targets index aeb820317be69..968ecd42a6ee8 100644 --- a/eng/targets/Imports.BeforeArcade.targets +++ b/eng/targets/Imports.BeforeArcade.targets @@ -17,9 +17,11 @@ 6.0.0 + 8.0.0-preview.3.23165.3 6.0.0 + 8.0.0-preview.3.23164.14 diff --git a/eng/targets/Imports.targets b/eng/targets/Imports.targets index 377a4315a1bd0..53eca693cbd1e 100644 --- a/eng/targets/Imports.targets +++ b/eng/targets/Imports.targets @@ -364,5 +364,12 @@ Condition="'@(ReferencesCopiedInThisBuild)' != ''"/> + + + + + + + diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props index 948df0e3d49b5..f00c32423d9f3 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -53,6 +53,64 @@ true + + + + + + $(NetPrevious) + $(SourceBuildToolsetTargetFramework) + $(NetCurrent);$(NetPrevious) + + + + + + + $(NetCurrent) + $(SourceBuildToolsetTargetFramework) + $(NetCurrent) + + + + + + + net6.0 + $(NetCurrent);$(NetPrevious);$(SourceBuildToolsetTargetFramework) + $(SourceBuildToolsetTargetFrameworks) + + + + + + + net6.0 + $(SourceBuildToolsetTargetFramework);net7.0 + $(SourceBuildToolsetTargetFrameworks) + + + + @@ -38,12 +38,13 @@ + + - diff --git a/src/Features/CSharp/Portable/StringIndentation/CSharpStringIndentationService.cs b/src/Features/CSharp/Portable/StringIndentation/CSharpStringIndentationService.cs index 9390ffcb7c1da..b7a4218b102c0 100644 --- a/src/Features/CSharp/Portable/StringIndentation/CSharpStringIndentationService.cs +++ b/src/Features/CSharp/Portable/StringIndentation/CSharpStringIndentationService.cs @@ -54,7 +54,7 @@ public CSharpStringIndentationService() while (nodeStack.TryPop(out var node)) { - // Dont' bother recursing into nodes that don't hit the requested span, they can never contribute + // DoNot' bother recursing into nodes that don't hit the requested span, they can never contribute // regions of interest. if (!node.Span.IntersectsWith(textSpan)) continue; diff --git a/src/EditorFeatures/CSharpTest/CodeActions/AddAwait/AddAwaitTests.cs b/src/Features/CSharpTest/AddAwait/AddAwaitTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/AddAwait/AddAwaitTests.cs rename to src/Features/CSharpTest/AddAwait/AddAwaitTests.cs diff --git a/src/EditorFeatures/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs b/src/Features/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs rename to src/Features/CSharpTest/AddDebuggerDisplay/AddDebuggerDisplayTests.cs diff --git a/src/EditorFeatures/CSharpTest/AddFileBanner/AddFileBannerTests.cs b/src/Features/CSharpTest/AddFileBanner/AddFileBannerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddFileBanner/AddFileBannerTests.cs rename to src/Features/CSharpTest/AddFileBanner/AddFileBannerTests.cs diff --git a/src/EditorFeatures/CSharpTest/AddFileBanner/AddFileBannerTests_FixAll.cs b/src/Features/CSharpTest/AddFileBanner/AddFileBannerTests_FixAll.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddFileBanner/AddFileBannerTests_FixAll.cs rename to src/Features/CSharpTest/AddFileBanner/AddFileBannerTests_FixAll.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AbstractAddUsingTests.cs b/src/Features/CSharpTest/AddUsing/AbstractAddUsingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AbstractAddUsingTests.cs rename to src/Features/CSharpTest/AddUsing/AbstractAddUsingTests.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs b/src/Features/CSharpTest/AddUsing/AddUsingNuGetTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs rename to src/Features/CSharpTest/AddUsing/AddUsingNuGetTests.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests.cs b/src/Features/CSharpTest/AddUsing/AddUsingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests.cs rename to src/Features/CSharpTest/AddUsing/AddUsingTests.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTestsWithAddImportDiagnosticProvider.cs b/src/Features/CSharpTest/AddUsing/AddUsingTestsWithAddImportDiagnosticProvider.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AddUsingTestsWithAddImportDiagnosticProvider.cs rename to src/Features/CSharpTest/AddUsing/AddUsingTestsWithAddImportDiagnosticProvider.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_ExtensionMethods.cs b/src/Features/CSharpTest/AddUsing/AddUsingTests_ExtensionMethods.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_ExtensionMethods.cs rename to src/Features/CSharpTest/AddUsing/AddUsingTests_ExtensionMethods.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_Queries.cs b/src/Features/CSharpTest/AddUsing/AddUsingTests_Queries.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_Queries.cs rename to src/Features/CSharpTest/AddUsing/AddUsingTests_Queries.cs diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_Razor.cs b/src/Features/CSharpTest/AddUsing/AddUsingTests_Razor.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_Razor.cs rename to src/Features/CSharpTest/AddUsing/AddUsingTests_Razor.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/AbstractCSharpCodeActionTest.cs b/src/Features/CSharpTest/CodeActions/AbstractCSharpCodeActionTest.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/AbstractCSharpCodeActionTest.cs rename to src/Features/CSharpTest/CodeActions/AbstractCSharpCodeActionTest.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ApplyChangesOperationTests.cs b/src/Features/CSharpTest/CodeActions/ApplyChangesOperationTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ApplyChangesOperationTests.cs rename to src/Features/CSharpTest/CodeActions/ApplyChangesOperationTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/Preview/ErrorCases/ExceptionInCodeAction.cs b/src/Features/CSharpTest/CodeActions/Preview/ErrorCases/ExceptionInCodeAction.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/Preview/ErrorCases/ExceptionInCodeAction.cs rename to src/Features/CSharpTest/CodeActions/Preview/ErrorCases/ExceptionInCodeAction.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.cs b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.cs rename to src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs b/src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs rename to src/Features/CSharpTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs b/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs rename to src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs b/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs rename to src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimInterpolatedStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimInterpolatedStringTests.cs rename to src/Features/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimInterpolatedStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimStringTests.cs b/src/Features/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimStringTests.cs rename to src/Features/CSharpTest/ConvertBetweenRegularAndVerbatimString/ConvertBetweenRegularAndVerbatimStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs rename to src/Features/CSharpTest/ConvertCast/ConvertDirectCastToTryCastTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs b/src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs rename to src/Features/CSharpTest/ConvertCast/ConvertTryCastToDirectCastTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs b/src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs rename to src/Features/CSharpTest/ConvertForEachToFor/ConvertForEachToForTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs b/src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs rename to src/Features/CSharpTest/ConvertForToForEach/ConvertForToForEachTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.cs rename to src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.cs rename to src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs b/src/Features/CSharpTest/ConvertLinq/ConvertForEachToLinqQueryTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs rename to src/Features/CSharpTest/ConvertLinq/ConvertForEachToLinqQueryTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs b/src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs rename to src/Features/CSharpTest/ConvertLinq/ConvertLinqQueryToForEachTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLocalFunctionToMethod/ConvertLocalFunctionToMethodTests.cs b/src/Features/CSharpTest/ConvertLocalFunctionToMethod/ConvertLocalFunctionToMethodTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ConvertLocalFunctionToMethod/ConvertLocalFunctionToMethodTests.cs rename to src/Features/CSharpTest/ConvertLocalFunctionToMethod/ConvertLocalFunctionToMethodTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringFixAllTests.cs b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringFixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringFixAllTests.cs rename to src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringFixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs b/src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs rename to src/Features/CSharpTest/ConvertNamespace/ConvertNamespaceRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.cs b/src/Features/CSharpTest/ConvertNumericLiteral/ConvertNumericLiteralTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.cs rename to src/Features/CSharpTest/ConvertNumericLiteral/ConvertNumericLiteralTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs rename to src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs rename to src/Features/CSharpTest/ConvertProgram/ConvertToProgramMainRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs rename to src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs b/src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs rename to src/Features/CSharpTest/ConvertProgram/ConvertToTopLevelStatementsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs rename to src/Features/CSharpTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs rename to src/Features/CSharpTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.cs b/src/Features/CSharpTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.cs rename to src/Features/CSharpTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs b/src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs rename to src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertToRawString/ConvertRegularStringToRawString_FixAllTests.cs b/src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawString_FixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertToRawString/ConvertRegularStringToRawString_FixAllTests.cs rename to src/Features/CSharpTest/ConvertToRawString/ConvertRegularStringToRawString_FixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs b/src/Features/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs rename to src/Features/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs index f46430139eb49..c2ca67d47ceb4 100644 --- a/src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs +++ b/src/Features/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs @@ -4278,7 +4278,7 @@ public static C GetC() } [Fact] - public async Task TestMovePropertiesAndDontRefactorInitializerWithExistingConstructor() + public async Task TestMovePropertiesAndDoNotRefactorInitializerWithExistingConstructor() { var initialMarkup = @" namespace N diff --git a/src/EditorFeatures/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs b/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs rename to src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs diff --git a/src/EditorFeatures/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs b/src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs rename to src/Features/CSharpTest/EmbeddedLanguages/JsonStringDetectorTests.cs diff --git a/src/EditorFeatures/CSharpTest/EmbeddedLanguages/ValidateJsonStringTests.cs b/src/Features/CSharpTest/EmbeddedLanguages/ValidateJsonStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/EmbeddedLanguages/ValidateJsonStringTests.cs rename to src/Features/CSharpTest/EmbeddedLanguages/ValidateJsonStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/EmbeddedLanguages/ValidateRegexStringTests.cs b/src/Features/CSharpTest/EmbeddedLanguages/ValidateRegexStringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/EmbeddedLanguages/ValidateRegexStringTests.cs rename to src/Features/CSharpTest/EmbeddedLanguages/ValidateRegexStringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableFixAllTests.cs b/src/Features/CSharpTest/EnableNullable/EnableNullableFixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableFixAllTests.cs rename to src/Features/CSharpTest/EnableNullable/EnableNullableFixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableTests.cs b/src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/EnableNullable/EnableNullableTests.cs rename to src/Features/CSharpTest/EnableNullable/EnableNullableTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/EncapsulateField/EncapsulateFieldTests.cs b/src/Features/CSharpTest/EncapsulateField/EncapsulateFieldTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/EncapsulateField/EncapsulateFieldTests.cs rename to src/Features/CSharpTest/EncapsulateField/EncapsulateFieldTests.cs diff --git a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs b/src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs rename to src/Features/CSharpTest/ExtractClass/ExtractClassTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractLocalFunctionTests.cs b/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractLocalFunctionTests.cs rename to src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs index f0f0483847c06..eeeeacc635dda 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractLocalFunctionTests.cs +++ b/src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs @@ -647,7 +647,7 @@ static void Main() } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractLocalFunction)] - public async Task DontOverparenthesize() + public async Task DoNotOverparenthesize() { await TestAsync( """ @@ -752,7 +752,7 @@ public static void Ex(this int x) } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractLocalFunction)] - public async Task DontOverparenthesizeGenerics() + public async Task DoNotOverparenthesizeGenerics() { await TestAsync( """ diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs rename to src/Features/CSharpTest/ExtractMethod/ExtractMethodTests.cs index 75bdfc9ab8b8f..11f0714e1f0d2 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs +++ b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests.cs @@ -877,7 +877,7 @@ static void Main() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530709")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/632182")] - public async Task DontOverparenthesize() + public async Task DoNotOverparenthesize() { await TestAsync( """ @@ -979,7 +979,7 @@ public static void Ex(this int x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/632182")] - public async Task DontOverparenthesizeGenerics() + public async Task DoNotOverparenthesizeGenerics() { await TestAsync( """ @@ -4365,7 +4365,7 @@ private static bool NewMethod() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40209")] - public async Task TestNaming_CamelCase_VerifyLocalFunctionSettingsDontApply() + public async Task TestNaming_CamelCase_VerifyLocalFunctionSettingsDoNotApply() { var input = """ @@ -4416,7 +4416,7 @@ private static bool NewMethod() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40209")] - public async Task TestNaming_CamelCase_VerifyLocalFunctionSettingsDontApply_GetName() + public async Task TestNaming_CamelCase_VerifyLocalFunctionSettingsDoNotApply_GetName() { var input = """ diff --git a/src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyTests.cs b/src/Features/CSharpTest/FullyQualify/FullyQualifyTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyTests.cs rename to src/Features/CSharpTest/FullyQualify/FullyQualifyTests.cs diff --git a/src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyUnboundIdentifierTests.cs b/src/Features/CSharpTest/FullyQualify/FullyQualifyUnboundIdentifierTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyUnboundIdentifierTests.cs rename to src/Features/CSharpTest/FullyQualify/FullyQualifyUnboundIdentifierTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.cs b/src/Features/CSharpTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.cs rename to src/Features/CSharpTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs b/src/Features/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs rename to src/Features/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs b/src/Features/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs rename to src/Features/CSharpTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateEnumMember/GenerateEnumMemberTests.cs b/src/Features/CSharpTest/GenerateEnumMember/GenerateEnumMemberTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateEnumMember/GenerateEnumMemberTests.cs rename to src/Features/CSharpTest/GenerateEnumMember/GenerateEnumMemberTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs b/src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs rename to src/Features/CSharpTest/GenerateFromMembers/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateConstructorFromMembers/GenerateConstructorFromMembersTests.cs b/src/Features/CSharpTest/GenerateFromMembers/GenerateConstructorFromMembers/GenerateConstructorFromMembersTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateConstructorFromMembers/GenerateConstructorFromMembersTests.cs rename to src/Features/CSharpTest/GenerateFromMembers/GenerateConstructorFromMembers/GenerateConstructorFromMembersTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs b/src/Features/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs rename to src/Features/CSharpTest/GenerateFromMembers/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateConversionTests.cs b/src/Features/CSharpTest/GenerateMethod/GenerateConversionTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateConversionTests.cs rename to src/Features/CSharpTest/GenerateMethod/GenerateConversionTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateDeconstructMethodTests.cs b/src/Features/CSharpTest/GenerateMethod/GenerateDeconstructMethodTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateDeconstructMethodTests.cs rename to src/Features/CSharpTest/GenerateMethod/GenerateDeconstructMethodTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs b/src/Features/CSharpTest/GenerateMethod/GenerateMethodTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs rename to src/Features/CSharpTest/GenerateMethod/GenerateMethodTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateOverrides/GenerateOverridesTests.cs b/src/Features/CSharpTest/GenerateOverrides/GenerateOverridesTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/GenerateOverrides/GenerateOverridesTests.cs rename to src/Features/CSharpTest/GenerateOverrides/GenerateOverridesTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs b/src/Features/CSharpTest/GenerateType/GenerateTypeTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs rename to src/Features/CSharpTest/GenerateType/GenerateTypeTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests_Dialog.cs b/src/Features/CSharpTest/GenerateType/GenerateTypeTests_Dialog.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests_Dialog.cs rename to src/Features/CSharpTest/GenerateType/GenerateTypeTests_Dialog.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeWithUnboundAnalyzerTests.cs b/src/Features/CSharpTest/GenerateType/GenerateTypeWithUnboundAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeWithUnboundAnalyzerTests.cs rename to src/Features/CSharpTest/GenerateType/GenerateTypeWithUnboundAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs b/src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs rename to src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs index 448ced797f5fb..9d5b656ce90e5 100644 --- a/src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs +++ b/src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs @@ -3029,7 +3029,7 @@ static void Main(string[] args) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543813")] - public async Task DontAddBlankLineBetweenFields() + public async Task DoNotAddBlankLineBetweenFields() { await TestInRegularAndScriptAsync( """ @@ -3061,7 +3061,7 @@ static void Main(string[] args) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543813")] - public async Task DontAddBlankLineBetweenAutoProperties() + public async Task DoNotAddBlankLineBetweenAutoProperties() { await TestInRegularAndScriptAsync( """ @@ -10583,7 +10583,7 @@ void Goo() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/45367")] - public async Task DontOfferPropertyOrFieldInNamespace() + public async Task DoNotOfferPropertyOrFieldInNamespace() { await TestExactActionSetOfferedAsync( """ diff --git a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs b/src/Features/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs rename to src/Features/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs diff --git a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.cs b/src/Features/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.cs rename to src/Features/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs b/src/Features/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs rename to src/Features/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests_ThroughMember.cs diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementExplicitlyTests.cs b/src/Features/CSharpTest/ImplementInterface/ImplementExplicitlyTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ImplementInterface/ImplementExplicitlyTests.cs rename to src/Features/CSharpTest/ImplementInterface/ImplementExplicitlyTests.cs diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementImplicitlyTests.cs b/src/Features/CSharpTest/ImplementInterface/ImplementImplicitlyTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ImplementInterface/ImplementImplicitlyTests.cs rename to src/Features/CSharpTest/ImplementInterface/ImplementImplicitlyTests.cs diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/Features/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs rename to src/Features/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs index dc3aa905a2d29..36c3f6b6be276 100644 --- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/Features/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs @@ -6279,7 +6279,7 @@ void I.F() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/941469")] - public async Task TestDontImplementDisposePatternForLocallyDefinedIDisposable() + public async Task TestDoNotImplementDisposePatternForLocallyDefinedIDisposable() { await TestWithAllCodeStyleOptionsOffAsync( """ @@ -6313,7 +6313,7 @@ void IDisposable.Dispose() } [Fact] - public async Task TestDontImplementDisposePatternForStructures1() + public async Task TestDoNotImplementDisposePatternForStructures1() { await TestWithAllCodeStyleOptionsOffAsync( """ @@ -6334,7 +6334,7 @@ public void Dispose() } [Fact] - public async Task TestDontImplementDisposePatternForStructures2() + public async Task TestDoNotImplementDisposePatternForStructures2() { await TestWithAllCodeStyleOptionsOffAsync( """ diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.cs b/src/Features/CSharpTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.cs rename to src/Features/CSharpTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs b/src/Features/CSharpTest/InitializeParameter/AddParameterCheckTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs rename to src/Features/CSharpTest/InitializeParameter/AddParameterCheckTests.cs diff --git a/src/EditorFeatures/CSharpTest/InitializeParameter/InitializeMemberFromParameterTests.cs b/src/Features/CSharpTest/InitializeParameter/InitializeMemberFromParameterTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InitializeParameter/InitializeMemberFromParameterTests.cs rename to src/Features/CSharpTest/InitializeParameter/InitializeMemberFromParameterTests.cs diff --git a/src/EditorFeatures/CSharpTest/InlineMethod/CSharpInlineMethodTests.cs b/src/Features/CSharpTest/InlineMethod/CSharpInlineMethodTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InlineMethod/CSharpInlineMethodTests.cs rename to src/Features/CSharpTest/InlineMethod/CSharpInlineMethodTests.cs diff --git a/src/EditorFeatures/CSharpTest/InlineMethod/CSharpInlineMethodTests_CrossLanguage.cs b/src/Features/CSharpTest/InlineMethod/CSharpInlineMethodTests_CrossLanguage.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InlineMethod/CSharpInlineMethodTests_CrossLanguage.cs rename to src/Features/CSharpTest/InlineMethod/CSharpInlineMethodTests_CrossLanguage.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs rename to src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index 47216586eecee..a53ead378e4d1 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -488,7 +488,7 @@ public async Task TestParenthesizedAtReference_Case3() } [Fact] - public async Task DontBreakOverloadResolution_Case5() + public async Task DoNotBreakOverloadResolution_Case5() { var code = """ class C @@ -521,7 +521,7 @@ void M() } [Fact] - public async Task DontTouchUnrelatedBlocks() + public async Task DoNotTouchUnrelatedBlocks() { var code = """ class C @@ -2440,7 +2440,7 @@ static void M() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544563")] - public async Task DontInlineStackAlloc() + public async Task DoNotInlineStackAlloc() { await TestMissingInRegularAndScriptAsync( """ @@ -2733,7 +2733,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545103")] - public async Task DontInsertCastForTypeThatNoLongerBindsToTheSameType() + public async Task DoNotInsertCastForTypeThatNoLongerBindsToTheSameType() { await TestAsync( """ @@ -2799,7 +2799,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545523")] - public async Task DontInsertCastForObjectCreationIfUnneeded() + public async Task DoNotInsertCastForObjectCreationIfUnneeded() { await TestInRegularAndScriptAsync( """ @@ -2827,7 +2827,7 @@ static void Main() } [Fact] - public async Task DontInsertCastInForeachIfUnneeded01() + public async Task DoNotInsertCastInForeachIfUnneeded01() { await TestInRegularAndScriptAsync( """ @@ -4009,7 +4009,7 @@ public static void M(string s) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4583")] - public async Task DontParenthesizeInterpolatedStringWithNoInterpolation_CSharp7() + public async Task DoNotParenthesizeInterpolatedStringWithNoInterpolation_CSharp7() { await TestInRegularAndScriptAsync( """ @@ -4090,7 +4090,7 @@ public void M() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4583")] - public async Task DontParenthesizeInterpolatedStringWithInterpolation_CSharp7() + public async Task DoNotParenthesizeInterpolatedStringWithInterpolation_CSharp7() { await TestInRegularAndScriptAsync( """ @@ -4116,7 +4116,7 @@ public void M(int x) [Fact(Skip = "https://github.com/dotnet/roslyn/issues/33108")] [WorkItem("https://github.com/dotnet/roslyn/issues/4583")] - public async Task DontParenthesizeInterpolatedStringWithInterpolation() + public async Task DoNotParenthesizeInterpolatedStringWithInterpolation() { await TestInRegularAndScriptAsync( """ diff --git a/src/EditorFeatures/CSharpTest/IntroduceParameter/IntroduceParameterTests.cs b/src/Features/CSharpTest/IntroduceParameter/IntroduceParameterTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/IntroduceParameter/IntroduceParameterTests.cs rename to src/Features/CSharpTest/IntroduceParameter/IntroduceParameterTests.cs diff --git a/src/EditorFeatures/CSharpTest/IntroduceUsingStatement/IntroduceUsingStatementTests.cs b/src/Features/CSharpTest/IntroduceUsingStatement/IntroduceUsingStatementTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/IntroduceUsingStatement/IntroduceUsingStatementTests.cs rename to src/Features/CSharpTest/IntroduceUsingStatement/IntroduceUsingStatementTests.cs diff --git a/src/EditorFeatures/CSharpTest/Interactive/CodeActions/InteractiveIntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/InteractiveIntroduceVariableTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Interactive/CodeActions/InteractiveIntroduceVariableTests.cs rename to src/Features/CSharpTest/IntroduceVariable/InteractiveIntroduceVariableTests.cs diff --git a/src/EditorFeatures/CSharpTest/IntroduceVariable/IntroduceLocalForExpressionTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceLocalForExpressionTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/IntroduceVariable/IntroduceLocalForExpressionTests.cs rename to src/Features/CSharpTest/IntroduceVariable/IntroduceLocalForExpressionTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs rename to src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index 49ec75daa941d..a10dacf61f5ed 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -3074,7 +3074,7 @@ void Goo() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/682683")] - public async Task DontRemoveParenthesesIfOperatorPrecedenceWouldBeBroken() + public async Task DoNotRemoveParenthesesIfOperatorPrecedenceWouldBeBroken() { await TestInRegularAndScriptAsync( """ @@ -8056,7 +8056,7 @@ class C } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40381")] - public async Task TestIntroduceFromMethod_AllOccurences_DontIncludeStaticLocalFunctionReferences() + public async Task TestIntroduceFromMethod_AllOccurences_DoNotIncludeStaticLocalFunctionReferences() { await TestInRegularAndScriptAsync( """ @@ -8105,7 +8105,7 @@ private static object Foo() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40381")] - public async Task TestIntroduceFromMethod_AllOccurences_DontIncludeStaticLocalFunctionReferences2() + public async Task TestIntroduceFromMethod_AllOccurences_DoNotIncludeStaticLocalFunctionReferences2() { await TestInRegularAndScriptAsync( """ diff --git a/src/EditorFeatures/CSharpTest/InvertConditional/InvertConditionalTests.cs b/src/Features/CSharpTest/InvertConditional/InvertConditionalTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InvertConditional/InvertConditionalTests.cs rename to src/Features/CSharpTest/InvertConditional/InvertConditionalTests.cs diff --git a/src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.Elseless.cs b/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.Elseless.cs rename to src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs diff --git a/src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.cs b/src/Features/CSharpTest/InvertIf/InvertIfTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InvertIf/InvertIfTests.cs rename to src/Features/CSharpTest/InvertIf/InvertIfTests.cs diff --git a/src/EditorFeatures/CSharpTest/InvertLogical/InvertLogicalTests.cs b/src/Features/CSharpTest/InvertLogical/InvertLogicalTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/InvertLogical/InvertLogicalTests.cs rename to src/Features/CSharpTest/InvertLogical/InvertLogicalTests.cs diff --git a/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs b/src/Features/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs rename to src/Features/CSharpTest/MakeLocalFunctionStatic/MakeLocalFunctionStaticRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs b/src/Features/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs rename to src/Features/CSharpTest/MakeLocalFunctionStatic/PassInCapturedVariablesAsArgumentsCodeFixProviderTests.cs diff --git a/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj b/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj new file mode 100644 index 0000000000000..b6570c7a73640 --- /dev/null +++ b/src/Features/CSharpTest/Microsoft.CodeAnalysis.CSharp.Features.UnitTests.csproj @@ -0,0 +1,54 @@ + + + + + Library + net472 + true + Microsoft.CodeAnalysis.Editor.CSharp.UnitTests + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/EditorFeatures/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs b/src/Features/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs rename to src/Features/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs diff --git a/src/EditorFeatures/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs b/src/Features/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs rename to src/Features/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs index 26366864031cb..c3fc3b44e03d6 100644 --- a/src/EditorFeatures/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs +++ b/src/Features/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs @@ -1849,7 +1849,7 @@ public static int TestMethod() } [Fact] - public async Task TestMoveExtensionMethodDontRefactor() + public async Task TestMoveExtensionMethodDoNotRefactor() { var initialMarkup = @" namespace TestNs1 diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/CSharpMoveTypeTestsBase.cs b/src/Features/CSharpTest/MoveType/CSharpMoveTypeTestsBase.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/MoveType/CSharpMoveTypeTestsBase.cs rename to src/Features/CSharpTest/MoveType/CSharpMoveTypeTestsBase.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.ActionCountTests.cs b/src/Features/CSharpTest/MoveType/MoveTypeTests.ActionCountTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.ActionCountTests.cs rename to src/Features/CSharpTest/MoveType/MoveTypeTests.ActionCountTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveScope.cs b/src/Features/CSharpTest/MoveType/MoveTypeTests.MoveScope.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveScope.cs rename to src/Features/CSharpTest/MoveType/MoveTypeTests.MoveScope.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs b/src/Features/CSharpTest/MoveType/MoveTypeTests.MoveToNewFile.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs rename to src/Features/CSharpTest/MoveType/MoveTypeTests.MoveToNewFile.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.RenameFile.cs b/src/Features/CSharpTest/MoveType/MoveTypeTests.RenameFile.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.RenameFile.cs rename to src/Features/CSharpTest/MoveType/MoveTypeTests.RenameFile.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.RenameType.cs b/src/Features/CSharpTest/MoveType/MoveTypeTests.RenameType.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.RenameType.cs rename to src/Features/CSharpTest/MoveType/MoveTypeTests.RenameType.cs diff --git a/src/EditorFeatures/CSharpTest/NameTupleElement/NameTupleElementTests.cs b/src/Features/CSharpTest/NameTupleElement/NameTupleElementTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/NameTupleElement/NameTupleElementTests.cs rename to src/Features/CSharpTest/NameTupleElement/NameTupleElementTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests.cs b/src/Features/CSharpTest/PreferFrameworkType/PreferFrameworkTypeTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests.cs rename to src/Features/CSharpTest/PreferFrameworkType/PreferFrameworkTypeTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.cs b/src/Features/CSharpTest/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.cs rename to src/Features/CSharpTest/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/PullMemberUp/CSharpPullMemberUpTests.cs b/src/Features/CSharpTest/PullMemberUp/CSharpPullMemberUpTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/PullMemberUp/CSharpPullMemberUpTests.cs rename to src/Features/CSharpTest/PullMemberUp/CSharpPullMemberUpTests.cs diff --git a/src/EditorFeatures/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs b/src/Features/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs similarity index 94% rename from src/EditorFeatures/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs rename to src/Features/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs index e19377947c6c4..44f89c8f4d145 100644 --- a/src/EditorFeatures/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs +++ b/src/Features/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs @@ -29,7 +29,7 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider => (new CSharpRemoveUnnecessaryCastDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryCastCodeFixProvider()); [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545979")] - public async Task DontRemoveCastToErrorType() + public async Task DoNotRemoveCastToErrorType() { await TestMissingInRegularAndScriptAsync( """ @@ -78,7 +78,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545138")] - public async Task DontRemoveTypeParameterCastToObject() + public async Task DoNotRemoveTypeParameterCastToObject() { await TestMissingInRegularAndScriptAsync( """ @@ -93,7 +93,7 @@ void Goo(T obj) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545139")] - public async Task DontRemoveCastInIsTest() + public async Task DoNotRemoveCastInIsTest() { await TestMissingInRegularAndScriptAsync( """ @@ -112,7 +112,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545142")] - public async Task DontRemoveCastNeedForUserDefinedOperator() + public async Task DoNotRemoveCastNeedForUserDefinedOperator() { await TestMissingInRegularAndScriptAsync( """ @@ -135,7 +135,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545143")] - public async Task DontRemovePointerCast1() + public async Task DoNotRemovePointerCast1() { await TestMissingInRegularAndScriptAsync( """ @@ -150,7 +150,7 @@ static unsafe void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545144")] - public async Task DontRemoveCastToObjectFromDelegateComparison() + public async Task DoNotRemoveCastToObjectFromDelegateComparison() { // The cast below can't be removed because it would result in the Delegate // op_Equality operator overload being used over reference equality. @@ -172,7 +172,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545145")] - public async Task DontRemoveCastToAnonymousMethodWhenOnLeftOfAsCast() + public async Task DoNotRemoveCastToAnonymousMethodWhenOnLeftOfAsCast() { await TestMissingInRegularAndScriptAsync( """ @@ -192,7 +192,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545157")] - public async Task DontRemoveIdentityCastWhichAffectsOverloadResolution1() + public async Task DoNotRemoveIdentityCastWhichAffectsOverloadResolution1() { await TestMissingInRegularAndScriptAsync( """ @@ -217,7 +217,7 @@ static void Goo(Func x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545158")] - public async Task DontRemoveIdentityCastWhichAffectsOverloadResolution2() + public async Task DoNotRemoveIdentityCastWhichAffectsOverloadResolution2() { await TestMissingInRegularAndScriptAsync( """ @@ -243,7 +243,7 @@ static void Goo(int x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545158")] - public async Task DontRemoveIdentityCastWhichAffectsOverloadResolution3() + public async Task DoNotRemoveIdentityCastWhichAffectsOverloadResolution3() { await TestMissingInRegularAndScriptAsync( """ @@ -270,7 +270,7 @@ static void Goo(int x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545747")] - public async Task DontRemoveCastWhichChangesTypeOfInferredLocal() + public async Task DoNotRemoveCastWhichChangesTypeOfInferredLocal() { await TestMissingInRegularAndScriptAsync( """ @@ -285,7 +285,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545159")] - public async Task DontRemoveNeededCastToIListOfObject() + public async Task DoNotRemoveNeededCastToIListOfObject() { await TestMissingInRegularAndScriptAsync( """ @@ -525,7 +525,7 @@ public static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545291")] - public async Task DontRemoveNeededCastInConditionalExpression() + public async Task DoNotRemoveNeededCastInConditionalExpression() { await TestMissingInRegularAndScriptAsync( """ @@ -625,7 +625,7 @@ static void Main() [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529787")] [WpfFact(Skip = "529787")] - public async Task DontRemoveNecessaryCastWhichInCollectionInitializer1() + public async Task DoNotRemoveNecessaryCastWhichInCollectionInitializer1() { await TestMissingInRegularAndScriptAsync( """ @@ -654,7 +654,7 @@ static void Main() [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529787")] [WpfFact(Skip = "529787")] - public async Task DontRemoveNecessaryCastWhichInCollectionInitializer2() + public async Task DoNotRemoveNecessaryCastWhichInCollectionInitializer2() { await TestMissingInRegularAndScriptAsync( """ @@ -709,7 +709,7 @@ static void Goo() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545608")] - public async Task DontRemoveNecessaryCastWithImplicitUserDefinedConversion() + public async Task DoNotRemoveNecessaryCastWithImplicitUserDefinedConversion() { await TestMissingInRegularAndScriptAsync( """ @@ -730,7 +730,7 @@ static void Goo() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545941")] - public async Task DontRemoveNecessaryCastWithImplicitConversionInThrow() + public async Task DoNotRemoveNecessaryCastWithImplicitConversionInThrow() { // The cast below can't be removed because the throw statement expects // an expression of type Exception -- not an expression convertible to @@ -756,7 +756,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545981")] - public async Task DontRemoveNecessaryCastInThrow() + public async Task DoNotRemoveNecessaryCastInThrow() { // The cast below can't be removed because the throw statement expects // an expression of type Exception -- not an expression convertible to @@ -807,7 +807,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545945")] - public async Task DontRemoveNecessaryDowncast() + public async Task DoNotRemoveNecessaryDowncast() { await TestMissingInRegularAndScriptAsync( """ @@ -822,7 +822,7 @@ void Goo(object y) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545606")] - public async Task DontRemoveNecessaryCastFromNullToTypeParameter() + public async Task DoNotRemoveNecessaryCastFromNullToTypeParameter() { await TestMissingInRegularAndScriptAsync( """ @@ -837,7 +837,7 @@ class X } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545744")] - public async Task DontRemoveNecessaryCastInImplicitlyTypedArray() + public async Task DoNotRemoveNecessaryCastInImplicitlyTypedArray() { await TestMissingInRegularAndScriptAsync( """ @@ -952,7 +952,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529816")] - public async Task DontRemoveNecessaryCastInQueryExpression() + public async Task DoNotRemoveNecessaryCastInQueryExpression() { await TestMissingInRegularAndScriptAsync( """ @@ -980,7 +980,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529831")] - public async Task DontRemoveNecessaryCastFromTypeParameterToInterface() + public async Task DoNotRemoveNecessaryCastFromTypeParameterToInterface() { await TestMissingInRegularAndScriptAsync( """ @@ -1121,7 +1121,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545877")] - public async Task DontCrashOnIncompleteMethodDeclaration() + public async Task DoNotCrashOnIncompleteMethodDeclaration() { await TestInRegularAndScriptAsync( """ @@ -1213,7 +1213,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529846")] - public async Task DontRemoveNecessaryCastFromTypeParameterToObject() + public async Task DoNotRemoveNecessaryCastFromTypeParameterToObject() { await TestMissingInRegularAndScriptAsync( """ @@ -1230,7 +1230,7 @@ static void Goo(T x, object y) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545858")] - public async Task DontRemoveNecessaryCastFromDelegateTypeToMulticastDelegate() + public async Task DoNotRemoveNecessaryCastFromDelegateTypeToMulticastDelegate() { await TestMissingInRegularAndScriptAsync( """ @@ -1249,7 +1249,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529842")] - public async Task DontRemoveNecessaryCastInTernaryExpression() + public async Task DoNotRemoveNecessaryCastInTernaryExpression() { await TestMissingInRegularAndScriptAsync( """ @@ -1321,7 +1321,7 @@ class C } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545957")] - public async Task DontRemoveCastInConstructorInitializer3() + public async Task DoNotRemoveCastInConstructorInitializer3() { await TestMissingInRegularAndScriptAsync( """ @@ -1386,7 +1386,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545962")] - public async Task DontRemoveCastWhenExpressionDoesntBind() + public async Task DoNotRemoveCastWhenExpressionDoesntBind() { // Note: The cast below can't be removed because its expression doesn't bind. @@ -1405,7 +1405,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545944")] - public async Task DontRemoveNecessaryCastBeforePointerDereference1() + public async Task DoNotRemoveNecessaryCastBeforePointerDereference1() { // Note: The cast below can't be removed because it would result in *null, // which is illegal. @@ -1420,7 +1420,7 @@ unsafe class C } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545978")] - public async Task DontRemoveNecessaryCastBeforePointerDereference2() + public async Task DoNotRemoveNecessaryCastBeforePointerDereference2() { // Note: The cast below can't be removed because it would result in dereferencing // void*, which is illegal. @@ -1439,7 +1439,7 @@ static void Main() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26640")] - public async Task DontRemoveCastToByteFromIntInConditionalExpression_CSharp8() + public async Task DoNotRemoveCastToByteFromIntInConditionalExpression_CSharp8() { await TestMissingInRegularAndScriptAsync( """ @@ -1454,7 +1454,7 @@ object M1(bool b) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26640")] - public async Task DontRemoveCastToByteFromIntInConditionalExpression_CSharp9() + public async Task DoNotRemoveCastToByteFromIntInConditionalExpression_CSharp9() { await TestMissingInRegularAndScriptAsync( """ @@ -1471,7 +1471,7 @@ object M1(bool b) #region Interface Casts [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545889")] - public async Task DontRemoveCastToInterfaceForUnsealedType() + public async Task DoNotRemoveCastToInterfaceForUnsealedType() { // Note: The cast below can't be removed because X is not sealed. @@ -1638,7 +1638,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545890")] - public async Task DontRemoveCastToInterfaceForSealedType4() + public async Task DoNotRemoveCastToInterfaceForSealedType4() { // Note: The cast below can't be removed (even though C is sealed) // because the unspecified optional parameter default values differ. @@ -1716,7 +1716,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545888")] - public async Task DontRemoveCastToInterfaceForSealedType6() + public async Task DoNotRemoveCastToInterfaceForSealedType6() { // Note: The cast below can't be removed (even though C is sealed) // because the specified named arguments refer to parameters that @@ -1777,7 +1777,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545888")] - public async Task DontRemoveCastToInterfaceForSealedType8() + public async Task DoNotRemoveCastToInterfaceForSealedType8() { // Note: The cast below can't be removed (even though C is sealed) // because the specified named arguments refer to parameters that @@ -1811,7 +1811,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545883")] - public async Task DontRemoveCastToInterfaceForSealedType9() + public async Task DoNotRemoveCastToInterfaceForSealedType9() { // Note: The cast below can't be removed (even though C is sealed) // because it would result in binding to a Dispose method that doesn't @@ -1839,7 +1839,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545887")] - public async Task DontRemoveCastToInterfaceForStruct1() + public async Task DoNotRemoveCastToInterfaceForStruct1() { // Note: The cast below can't be removed because the cast boxes 's' and // unboxing would change program behavior. @@ -2027,7 +2027,7 @@ static void Main() #region ParamArray Parameter Casts [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545141")] - public async Task DontRemoveCastToObjectInParamArrayArg1() + public async Task DoNotRemoveCastToObjectInParamArrayArg1() { await TestMissingInRegularAndScriptAsync( """ @@ -2049,7 +2049,7 @@ static void Goo(params object[] x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529911")] - public async Task DontRemoveCastToIntArrayInParamArrayArg2() + public async Task DoNotRemoveCastToIntArrayInParamArrayArg2() { await TestMissingInRegularAndScriptAsync( """ @@ -2071,7 +2071,7 @@ static void Goo(params object[] x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529911")] - public async Task DontRemoveCastToObjectArrayInParamArrayArg3() + public async Task DoNotRemoveCastToObjectArrayInParamArrayArg3() { await TestMissingInRegularAndScriptAsync( """ @@ -2270,7 +2270,7 @@ static void Main() #region ForEach Statements [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545961")] - public async Task DontRemoveNecessaryCastInForEach1() + public async Task DoNotRemoveNecessaryCastInForEach1() { // The cast below can't be removed because it would result an error // in the foreach statement. @@ -2293,7 +2293,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545961")] - public async Task DontRemoveNecessaryCastInForEach2() + public async Task DoNotRemoveNecessaryCastInForEach2() { // The cast below can't be removed because it would result an error // in the foreach statement. @@ -2316,7 +2316,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545961")] - public async Task DontRemoveNecessaryCastInForEach3() + public async Task DoNotRemoveNecessaryCastInForEach3() { // The cast below can't be removed because it would result an error // in the foreach statement since C doesn't contain a GetEnumerator() @@ -2353,7 +2353,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545961")] - public async Task DontRemoveNecessaryCastInForEach4() + public async Task DoNotRemoveNecessaryCastInForEach4() { // The cast below can't be removed because it would result in // C.GetEnumerator() being called rather than D.GetEnumerator(). @@ -2396,7 +2396,7 @@ static void Main() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545961")] - public async Task DontRemoveNecessaryCastInForEach5() + public async Task DoNotRemoveNecessaryCastInForEach5() { // The cast below can't be removed because it would change the // type of 'x'. @@ -2425,7 +2425,7 @@ static void Main() #endregion [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545925")] - public async Task DontRemoveCastIfOverriddenMethodHasIncompatibleParameterList() + public async Task DoNotRemoveCastIfOverriddenMethodHasIncompatibleParameterList() { // Note: The cast below can't be removed because the parameter list // of Goo and its override have different default values. @@ -2568,7 +2568,7 @@ static async Task Goo() [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/624252")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/608180")] - public async Task DontRemoveCastIfArgumentIsRestricted_TypedReference() + public async Task DoNotRemoveCastIfArgumentIsRestricted_TypedReference() { await TestMissingInRegularAndScriptAsync( """ @@ -2594,7 +2594,7 @@ static void dd(object obj, TypedReference d) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithOtherDynamicArguments() + public async Task DoNotRemoveCastOnArgumentsWithOtherDynamicArguments() { await TestMissingInRegularAndScriptAsync( """ @@ -2628,7 +2628,7 @@ static bool Goo(int x, object y, object z) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithOtherDynamicArguments_Bracketed() + public async Task DoNotRemoveCastOnArgumentsWithOtherDynamicArguments_Bracketed() { await TestMissingInRegularAndScriptAsync( """ @@ -2667,7 +2667,7 @@ void Goo(dynamic xx) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithDynamicReceiverOpt() + public async Task DoNotRemoveCastOnArgumentsWithDynamicReceiverOpt() { await TestMissingInRegularAndScriptAsync( """ @@ -2683,7 +2683,7 @@ static bool Goo(dynamic d) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithDynamicReceiverOpt_1() + public async Task DoNotRemoveCastOnArgumentsWithDynamicReceiverOpt_1() { await TestMissingInRegularAndScriptAsync( """ @@ -2699,7 +2699,7 @@ static bool Goo(dynamic d) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithDynamicReceiverOpt_2() + public async Task DoNotRemoveCastOnArgumentsWithDynamicReceiverOpt_2() { await TestMissingInRegularAndScriptAsync( """ @@ -2715,7 +2715,7 @@ static bool Goo(dynamic d) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithDynamicReceiverOpt_3() + public async Task DoNotRemoveCastOnArgumentsWithDynamicReceiverOpt_3() { await TestMissingInRegularAndScriptAsync( """ @@ -2731,7 +2731,7 @@ static bool Goo(dynamic d) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/627107")] - public async Task DontRemoveCastOnArgumentsWithOtherDynamicArguments_1() + public async Task DoNotRemoveCastOnArgumentsWithOtherDynamicArguments_1() { await TestMissingInRegularAndScriptAsync( """ @@ -2765,7 +2765,7 @@ static bool Goo(object y, int x, object z) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529846")] - public async Task DontUnnecessaryCastFromTypeParameterToObject() + public async Task DoNotUnnecessaryCastFromTypeParameterToObject() { await TestMissingInRegularAndScriptAsync( """ @@ -2813,7 +2813,7 @@ void Goo(Task x) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/626026")] - public async Task DontRemoveCastIfUserDefinedExplicitCast() + public async Task DoNotRemoveCastIfUserDefinedExplicitCast() { await TestMissingInRegularAndScriptAsync( """ @@ -2841,7 +2841,7 @@ public struct B } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/770187")] - public async Task DontRemoveNecessaryCastInSwitchExpression() + public async Task DoNotRemoveNecessaryCastInSwitchExpression() { await TestMissingInRegularAndScriptAsync( """ @@ -2873,7 +2873,7 @@ enum E [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2761")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/844482")] - public async Task DontRemoveCastFromBaseToDerivedWithExplicitReference() + public async Task DoNotRemoveCastFromBaseToDerivedWithExplicitReference() { await TestMissingInRegularAndScriptAsync( """ @@ -2898,7 +2898,7 @@ class D : C } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/3254")] - public async Task DontRemoveCastToTypeParameterWithExceptionConstraint() + public async Task DoNotRemoveCastToTypeParameterWithExceptionConstraint() { await TestMissingInRegularAndScriptAsync( """ @@ -2918,7 +2918,7 @@ class Program } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/3254")] - public async Task DontRemoveCastToTypeParameterWithExceptionSubTypeConstraint() + public async Task DoNotRemoveCastToTypeParameterWithExceptionSubTypeConstraint() { await TestMissingInRegularAndScriptAsync( """ @@ -2938,7 +2938,7 @@ class Program } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/8111")] - public async Task DontRemoveCastThatChangesShapeOfAnonymousTypeObject() + public async Task DoNotRemoveCastThatChangesShapeOfAnonymousTypeObject() { await TestMissingInRegularAndScriptAsync( """ @@ -2978,7 +2978,7 @@ static void Main(string o) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/18978")] - public async Task DontRemoveCastOnCallToMethodWithParamsArgs() + public async Task DoNotRemoveCastOnCallToMethodWithParamsArgs() { await TestMissingInRegularAndScriptAsync( """ @@ -3089,7 +3089,7 @@ private static void TakesParams(params object[] goo) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20630")] - public async Task DontRemoveCastOnCallToAttributeWithParamsArgs() + public async Task DoNotRemoveCastOnCallToAttributeWithParamsArgs() { await TestMissingInRegularAndScriptAsync( """ @@ -3154,7 +3154,7 @@ static void Main() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20630")] - public async Task DontRemoveCastOnCallToAttributeWithParamsArgsAndProperty() + public async Task DoNotRemoveCastOnCallToAttributeWithParamsArgsAndProperty() { await TestMissingInRegularAndScriptAsync( """ @@ -3175,7 +3175,7 @@ static class Program } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20630")] - public async Task DontRemoveCastOnCallToAttributeWithParamsArgsPropertyAndOtherArg() + public async Task DoNotRemoveCastOnCallToAttributeWithParamsArgsPropertyAndOtherArg() { await TestMissingInRegularAndScriptAsync( """ @@ -3196,7 +3196,7 @@ static class Program } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20630")] - public async Task DontRemoveCastOnCallToAttributeWithParamsArgsNamedArgsAndProperty() + public async Task DoNotRemoveCastOnCallToAttributeWithParamsArgsNamedArgsAndProperty() { await TestMissingInRegularAndScriptAsync( """ @@ -3314,7 +3314,7 @@ static class Program } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25456#issuecomment-373549735")] - public async Task DontIntroduceDefaultLiteralInSwitchCase() + public async Task DoNotIntroduceDefaultLiteralInSwitchCase() { await TestMissingInRegularAndScriptAsync( """ @@ -3333,7 +3333,7 @@ void M() } [Fact] - public async Task DontIntroduceDefaultLiteralInSwitchCase_CastInsideParentheses() + public async Task DoNotIntroduceDefaultLiteralInSwitchCase_CastInsideParentheses() { await TestMissingInRegularAndScriptAsync( """ @@ -3352,7 +3352,7 @@ void M() } [Fact] - public async Task DontIntroduceDefaultLiteralInSwitchCase_DefaultInsideParentheses() + public async Task DoNotIntroduceDefaultLiteralInSwitchCase_DefaultInsideParentheses() { await TestMissingInRegularAndScriptAsync( """ @@ -3371,7 +3371,7 @@ void M() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27239")] - public async Task DontOfferToRemoveCastWhereNoConversionExists() + public async Task DoNotOfferToRemoveCastWhereNoConversionExists() { await TestMissingInRegularAndScriptAsync( """ @@ -3389,7 +3389,7 @@ void M() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/28412")] - public async Task DontOfferToRemoveCastWhenAccessingHiddenProperty() + public async Task DoNotOfferToRemoveCastWhenAccessingHiddenProperty() { await TestMissingInRegularAndScriptAsync(""" using System.Collections.Generic; diff --git a/src/EditorFeatures/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs b/src/Features/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs similarity index 99% rename from src/EditorFeatures/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs rename to src/Features/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs index 028a3bb045f79..8175c6bdee163 100644 --- a/src/EditorFeatures/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs +++ b/src/Features/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs @@ -815,7 +815,7 @@ void Method() [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)] [WorkItem("https://github.com/dotnet/roslyn/issues/49827")] - public async Task DontCrashOnDeclarationInsideIfStatement() + public async Task DoNotCrashOnDeclarationInsideIfStatement() { await TestMissingInRegularAndScriptAsync( """ diff --git a/src/EditorFeatures/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs b/src/Features/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs rename to src/Features/CSharpTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.cs diff --git a/src/EditorFeatures/CSharpTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.cs b/src/Features/CSharpTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.cs rename to src/Features/CSharpTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs b/src/Features/CSharpTest/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs rename to src/Features/CSharpTest/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs b/src/Features/CSharpTest/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs rename to src/Features/CSharpTest/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs diff --git a/src/EditorFeatures/CSharpTest/ReverseForStatement/ReverseForStatementTests.cs b/src/Features/CSharpTest/ReverseForStatement/ReverseForStatementTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/ReverseForStatement/ReverseForStatementTests.cs rename to src/Features/CSharpTest/ReverseForStatement/ReverseForStatementTests.cs diff --git a/src/EditorFeatures/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs b/src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs rename to src/Features/CSharpTest/SimplifyPropertyPattern/SimplifyPropertyPatternTests.cs diff --git a/src/EditorFeatures/CSharpTest/SimplifyThisOrMe/SimplifyThisOrMeTests.cs b/src/Features/CSharpTest/SimplifyThisOrMe/SimplifyThisOrMeTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SimplifyThisOrMe/SimplifyThisOrMeTests.cs rename to src/Features/CSharpTest/SimplifyThisOrMe/SimplifyThisOrMeTests.cs diff --git a/src/EditorFeatures/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs b/src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs rename to src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests.cs diff --git a/src/EditorFeatures/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs b/src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs rename to src/Features/CSharpTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs b/src/Features/CSharpTest/SpellCheck/SpellCheckTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Diagnostics/SpellCheck/SpellCheckTests.cs rename to src/Features/CSharpTest/SpellCheck/SpellCheckTests.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_Middle.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_Middle.cs similarity index 95% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_Middle.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_Middle.cs index 7d143f36ddf5b..2fb846cb8ca02 100644 --- a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_Middle.cs +++ b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_Middle.cs @@ -22,10 +22,10 @@ void M(bool a, bool b, bool c) { if (a) System.Console.WriteLine(null); - [||]else if (b) + [||]else {|applicableSpan:if (b) System.Console.WriteLine(); else if (c) - System.Console.WriteLine(); + System.Console.WriteLine();|} } } """; @@ -45,6 +45,7 @@ void M(bool a, bool b, bool c) await TestActionCountAsync(Initial, 1); await TestInRegularAndScriptAsync(Initial, Expected); + await TestCodeRefactoringApplicableTextSpan(Initial, "applicableSpan"); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_Middle.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_Middle.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_Middle.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_Middle.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.cs diff --git a/src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.cs b/src/Features/CSharpTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.cs rename to src/Features/CSharpTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs b/src/Features/CSharpTest/SyncNamespace/CSharpSyncNamespaceTestsBase.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs rename to src/Features/CSharpTest/SyncNamespace/CSharpSyncNamespaceTestsBase.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs b/src/Features/CSharpTest/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs rename to src/Features/CSharpTest/SyncNamespace/SyncNamespaceTests_ChangeNamespace.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_MoveFile.cs b/src/Features/CSharpTest/SyncNamespace/SyncNamespaceTests_MoveFile.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_MoveFile.cs rename to src/Features/CSharpTest/SyncNamespace/SyncNamespaceTests_MoveFile.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_NoAction.cs b/src/Features/CSharpTest/SyncNamespace/SyncNamespaceTests_NoAction.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/SyncNamespaceTests_NoAction.cs rename to src/Features/CSharpTest/SyncNamespace/SyncNamespaceTests_NoAction.cs diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseExplicitOrImplicitType/UseExplicitTypeRefactoringTests.cs b/src/Features/CSharpTest/UseExplicitOrImplicitType/UseExplicitTypeRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeRefactorings/UseExplicitOrImplicitType/UseExplicitTypeRefactoringTests.cs rename to src/Features/CSharpTest/UseExplicitOrImplicitType/UseExplicitTypeRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs b/src/Features/CSharpTest/UseExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeRefactorings/UseExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs rename to src/Features/CSharpTest/UseExplicitOrImplicitType/UseImplicitTypeRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyFixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBodyForLambda/UseExpressionBodyForLambdasRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBodyForLambda/UseExpressionBodyForLambdasRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBodyForLambda/UseExpressionBodyForLambdasRefactoringTests.cs rename to src/Features/CSharpTest/UseExpressionBodyForLambda/UseExpressionBodyForLambdasRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs b/src/Features/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs rename to src/Features/CSharpTest/UseNamedArguments/UseNamedArgumentsTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs b/src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs rename to src/Features/CSharpTest/UseNameofInAttribute/UseNameofInAttributeTests.cs diff --git a/src/EditorFeatures/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs b/src/Features/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs rename to src/Features/CSharpTest/UsePatternCombinators/CSharpUsePatternCombinatorsDiagnosticAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzerTests.cs b/src/Features/CSharpTest/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzerTests.cs rename to src/Features/CSharpTest/UsePatternMatching/CSharpIsAndCastCheckWithoutNameDiagnosticAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringFixAllTests.cs b/src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringFixAllTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringFixAllTests.cs rename to src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringFixAllTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs b/src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs rename to src/Features/CSharpTest/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/Utilities/CodeSnippets.cs b/src/Features/CSharpTest/Utilities/CodeSnippets.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Utilities/CodeSnippets.cs rename to src/Features/CSharpTest/Utilities/CodeSnippets.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs b/src/Features/CSharpTest/Wrapping/AbstractWrappingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs rename to src/Features/CSharpTest/Wrapping/AbstractWrappingTests.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/ArgumentWrappingTests.cs b/src/Features/CSharpTest/Wrapping/ArgumentWrappingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/ArgumentWrappingTests.cs rename to src/Features/CSharpTest/Wrapping/ArgumentWrappingTests.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/BinaryExpressionWrappingTests.cs b/src/Features/CSharpTest/Wrapping/BinaryExpressionWrappingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/BinaryExpressionWrappingTests.cs rename to src/Features/CSharpTest/Wrapping/BinaryExpressionWrappingTests.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/ChainedExpressionWrappingTests.cs b/src/Features/CSharpTest/Wrapping/ChainedExpressionWrappingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/ChainedExpressionWrappingTests.cs rename to src/Features/CSharpTest/Wrapping/ChainedExpressionWrappingTests.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs b/src/Features/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs rename to src/Features/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/ParameterWrappingTests.cs b/src/Features/CSharpTest/Wrapping/ParameterWrappingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/ParameterWrappingTests.cs rename to src/Features/CSharpTest/Wrapping/ParameterWrappingTests.cs diff --git a/src/EditorFeatures/CSharpTest/Wrapping/SortingTests.cs b/src/Features/CSharpTest/Wrapping/SortingTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/Wrapping/SortingTests.cs rename to src/Features/CSharpTest/Wrapping/SortingTests.cs diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs index 3ac71423b81fc..b8b7fddb8782b 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -16,14 +17,11 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Packaging; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using Microsoft.CodeAnalysis.CodeGeneration; -using Microsoft.CodeAnalysis.CodeCleanup; namespace Microsoft.CodeAnalysis.AddImport { @@ -31,6 +29,12 @@ internal abstract partial class AbstractAddImportFeatureService where TSimpleNameSyntax : SyntaxNode { + /// + /// Cache of information about whether a is likely contained within a + /// NuGet packages directory. + /// + private static readonly ConditionalWeakTable> s_isInPackagesDirectory = new(); + protected abstract bool CanAddImport(SyntaxNode node, bool allowInHiddenRegions, CancellationToken cancellationToken); protected abstract bool CanAddImportForMethod(string diagnosticId, ISyntaxFacts syntaxFacts, SyntaxNode node, out TSimpleNameSyntax nameNode); protected abstract bool CanAddImportForNamespace(string diagnosticId, SyntaxNode node, out TSimpleNameSyntax nameNode); @@ -90,7 +94,6 @@ internal abstract partial class AbstractAddImportFeatureService.GetInstance(out var result); if (node != null) { - using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) @@ -102,13 +105,15 @@ internal abstract partial class AbstractAddImportFeatureService maxResults) + break; } } } @@ -135,9 +140,7 @@ internal abstract partial class AbstractAddImportFeatureService 0) - { return exactReferences; - } // No exact matches found. Fall back to fuzzy searching. // Only bother doing this for host workspaces. We don't want this for @@ -161,12 +164,12 @@ private static bool IsHostOrRemoteWorkspace(Project project) ConcurrentDictionary referenceToCompilation, Project project, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var allReferences); + var allReferences = new ConcurrentQueue(); // First search the current project to see if any symbols (source or metadata) match the // search string. await FindResultsInAllSymbolsInStartingProjectAsync( - allReferences, maxResults, finder, exact, cancellationToken).ConfigureAwait(false); + allReferences, finder, exact, cancellationToken).ConfigureAwait(false); // Only bother doing this for host workspaces. We don't want this for // things like the Interactive workspace as we can't even add project @@ -188,38 +191,35 @@ private static bool IsHostOrRemoteWorkspace(Project project) } } - return allReferences.ToImmutable(); + return allReferences.ToImmutableArray(); } private static async Task FindResultsInAllSymbolsInStartingProjectAsync( - ArrayBuilder allSymbolReferences, int maxResults, SymbolReferenceFinder finder, - bool exact, CancellationToken cancellationToken) + ConcurrentQueue allSymbolReferences, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { - var references = await finder.FindInAllSymbolsInStartingProjectAsync(exact, cancellationToken).ConfigureAwait(false); - AddRange(allSymbolReferences, references, maxResults); + AddRange( + allSymbolReferences, + await finder.FindInAllSymbolsInStartingProjectAsync(exact, cancellationToken).ConfigureAwait(false)); } private static async Task FindResultsInUnreferencedProjectSourceSymbolsAsync( ConcurrentDictionary> projectToAssembly, - Project project, ArrayBuilder allSymbolReferences, int maxResults, + Project project, ConcurrentQueue allSymbolReferences, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { // If we didn't find enough hits searching just in the project, then check // in any unreferenced projects. if (allSymbolReferences.Count >= maxResults) - { return; - } var viableUnreferencedProjects = GetViableUnreferencedProjects(project); // Search all unreferenced projects in parallel. - var findTasks = new HashSet>>(); + using var _ = ArrayBuilder.GetInstance(out var findTasks); // Create another cancellation token so we can both search all projects in parallel, // but also stop any searches once we get enough results. - using var nestedTokenSource = new CancellationTokenSource(); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); foreach (var unreferencedProject in viableUnreferencedProjects) { @@ -230,24 +230,23 @@ private static bool IsHostOrRemoteWorkspace(Project project) // direct references. i.e. we don't want to search in its metadata references // or in the projects it references itself. We'll be searching those entities // individually. - findTasks.Add(finder.FindInSourceSymbolsInProjectAsync( - projectToAssembly, unreferencedProject, exact, linkedTokenSource.Token)); + findTasks.Add(ProcessReferencesAsync( + allSymbolReferences, maxResults, linkedTokenSource, + finder.FindInSourceSymbolsInProjectAsync(projectToAssembly, unreferencedProject, exact, linkedTokenSource.Token))); } - await WaitForTasksAsync(allSymbolReferences, maxResults, findTasks, nestedTokenSource, cancellationToken).ConfigureAwait(false); + await Task.WhenAll(findTasks).ConfigureAwait(false); } private async Task FindResultsInUnreferencedMetadataSymbolsAsync( ConcurrentDictionary referenceToCompilation, - Project project, ArrayBuilder allSymbolReferences, int maxResults, SymbolReferenceFinder finder, + Project project, ConcurrentQueue allSymbolReferences, int maxResults, SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) { + // Only do this if none of the project searches produced any results. We may have a + // lot of metadata to search through, and it would be good to avoid that if we can. if (allSymbolReferences.Count > 0) - { - // Only do this if none of the project searches produced any results. We may have a - // lot of metadata to search through, and it would be good to avoid that if we can. return; - } // Keep track of the references we've seen (so that we don't process them multiple times // across many sibling projects). Prepopulate it with our own metadata references since @@ -258,12 +257,11 @@ private static bool IsHostOrRemoteWorkspace(Project project) var newReferences = GetUnreferencedMetadataReferences(project, seenReferences); // Search all metadata references in parallel. - var findTasks = new HashSet>>(); + using var _ = ArrayBuilder.GetInstance(out var findTasks); // Create another cancellation token so we can both search all projects in parallel, // but also stop any searches once we get enough results. - using var nestedTokenSource = new CancellationTokenSource(); - using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(nestedTokenSource.Token, cancellationToken); + using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); foreach (var (referenceProject, reference) in newReferences) { @@ -274,12 +272,13 @@ private static bool IsHostOrRemoteWorkspace(Project project) // Second, the SymbolFinder API doesn't even support searching them. if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) { - findTasks.Add(finder.FindInMetadataSymbolsAsync( - assembly, referenceProject, reference, exact, linkedTokenSource.Token)); + findTasks.Add(ProcessReferencesAsync( + allSymbolReferences, maxResults, linkedTokenSource, + finder.FindInMetadataSymbolsAsync(assembly, referenceProject, reference, exact, linkedTokenSource.Token))); } } - await WaitForTasksAsync(allSymbolReferences, maxResults, findTasks, nestedTokenSource, cancellationToken).ConfigureAwait(false); + await Task.WhenAll(findTasks).ConfigureAwait(false); } /// @@ -314,38 +313,25 @@ private static bool IsHostOrRemoteWorkspace(Project project) return result.ToImmutableAndFree(); } - private static async Task WaitForTasksAsync( - ArrayBuilder allSymbolReferences, + private static async Task ProcessReferencesAsync( + ConcurrentQueue allSymbolReferences, int maxResults, - HashSet>> findTasks, - CancellationTokenSource nestedTokenSource, - CancellationToken cancellationToken) + CancellationTokenSource linkedTokenSource, + Task> task) { - try + AddRange(allSymbolReferences, await task.ConfigureAwait(false)); + + // If we've gone over the max amount of items we're looking for, attempt to cancel all existing work that is + // still searching. + if (allSymbolReferences.Count >= maxResults) { - while (findTasks.Count > 0) + try + { + linkedTokenSource.Cancel(); + } + catch (ObjectDisposedException) { - // Keep on looping through the 'find' tasks, processing each when they finish. - cancellationToken.ThrowIfCancellationRequested(); - var doneTask = await Task.WhenAny(findTasks).ConfigureAwait(false); - - // One of the tasks finished. Remove it from the list we're waiting on. - findTasks.Remove(doneTask); - - // Add its results to the final result set we're keeping. - AddRange(allSymbolReferences, await doneTask.ConfigureAwait(false), maxResults); - - // Once we get enough, just stop. - if (allSymbolReferences.Count >= maxResults) - { - return; - } } - } - finally - { - // Cancel any nested work that's still happening. - nestedTokenSource.Cancel(); } } @@ -376,10 +362,17 @@ private static bool IsHostOrRemoteWorkspace(Project project) /// private static bool IsInPackagesDirectory(PortableExecutableReference reference) { - return ContainsPathComponent(reference, "packages") - || ContainsPathComponent(reference, "packs") - || ContainsPathComponent(reference, "NuGetFallbackFolder") - || ContainsPathComponent(reference, "NuGetPackages"); + return s_isInPackagesDirectory.GetValue( + reference, + static reference => new StrongBox(ComputeIsInPackagesDirectory(reference))).Value; + + static bool ComputeIsInPackagesDirectory(PortableExecutableReference reference) + { + return ContainsPathComponent(reference, "packages") + || ContainsPathComponent(reference, "packs") + || ContainsPathComponent(reference, "NuGetFallbackFolder") + || ContainsPathComponent(reference, "NuGetPackages"); + } static bool ContainsPathComponent(PortableExecutableReference reference, string pathComponent) { @@ -442,10 +435,10 @@ private static HashSet GetViableUnreferencedProjects(Project project) return viableProjects; } - private static void AddRange(ArrayBuilder allSymbolReferences, ImmutableArray proposedReferences, int maxResults) - where TReference : Reference + private static void AddRange(ConcurrentQueue allSymbolReferences, ImmutableArray proposedReferences) { - allSymbolReferences.AddRange(proposedReferences.Take(maxResults - allSymbolReferences.Count)); + foreach (var reference in proposedReferences) + allSymbolReferences.Enqueue(reference); } protected static bool IsViableExtensionMethod(IMethodSymbol method, ITypeSymbol receiver) diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs index f17a00a205ede..19c8c01f38dc9 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -23,17 +21,16 @@ private class AllSymbolsProjectSearchScope : ProjectSearchScope public AllSymbolsProjectSearchScope( AbstractAddImportFeatureService provider, Project project, - bool exact, - CancellationToken cancellationToken) - : base(provider, project, exact, cancellationToken) + bool exact) + : base(provider, project, exact) { } protected override async Task> FindDeclarationsAsync( - SymbolFilter filter, SearchQuery searchQuery) + SymbolFilter filter, SearchQuery searchQuery, CancellationToken cancellationToken) { var declarations = await DeclarationFinder.FindAllDeclarationsWithNormalQueryAsync( - _project, searchQuery, filter, CancellationToken).ConfigureAwait(false); + _project, searchQuery, filter, cancellationToken).ConfigureAwait(false); return declarations; } diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs index 769ae7b42d55d..ef0de1bbe662e 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; -using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.AddImport { @@ -26,9 +23,8 @@ private class MetadataSymbolsSearchScope : SearchScope Project assemblyProject, IAssemblySymbol assembly, PortableExecutableReference metadataReference, - bool exact, - CancellationToken cancellationToken) - : base(provider, exact, cancellationToken) + bool exact) + : base(provider, exact) { _assemblyProject = assemblyProject; _assembly = assembly; @@ -45,15 +41,15 @@ public override SymbolReference CreateReference(SymbolResult searchResult) } protected override async Task> FindDeclarationsAsync( - SymbolFilter filter, SearchQuery searchQuery) + SymbolFilter filter, SearchQuery searchQuery, CancellationToken cancellationToken) { - var service = _assemblyProject.Solution.Services.GetService(); - var info = await service.TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(_assemblyProject, _metadataReference, CancellationToken).ConfigureAwait(false); + var service = _assemblyProject.Solution.Services.GetRequiredService(); + var info = await service.TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(_assemblyProject, _metadataReference, cancellationToken).ConfigureAwait(false); if (info == null) return ImmutableArray.Empty; var declarations = await info.FindAsync( - searchQuery, _assembly, filter, CancellationToken).ConfigureAwait(false); + searchQuery, _assembly, filter, cancellationToken).ConfigureAwait(false); return declarations; } diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs index 7f464fc831898..1011a9a00652e 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using System.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport @@ -18,9 +15,8 @@ private abstract class ProjectSearchScope : SearchScope public ProjectSearchScope( AbstractAddImportFeatureService provider, Project project, - bool exact, - CancellationToken cancellationToken) - : base(provider, exact, cancellationToken) + bool exact) + : base(provider, exact) { Contract.ThrowIfFalse(project.SupportsCompilation); _project = project; diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs index 7305640644ed7..334ff70c0e022 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs @@ -26,28 +26,28 @@ private abstract class SearchScope { public readonly bool Exact; protected readonly AbstractAddImportFeatureService provider; - public readonly CancellationToken CancellationToken; - protected SearchScope(AbstractAddImportFeatureService provider, bool exact, CancellationToken cancellationToken) + protected SearchScope(AbstractAddImportFeatureService provider, bool exact) { this.provider = provider; Exact = exact; - CancellationToken = cancellationToken; } - protected abstract Task> FindDeclarationsAsync(SymbolFilter filter, SearchQuery query); + protected abstract Task> FindDeclarationsAsync(SymbolFilter filter, SearchQuery query, CancellationToken cancellationToken); + public abstract SymbolReference CreateReference(SymbolResult symbol) where T : INamespaceOrTypeSymbol; public async Task>> FindDeclarationsAsync( - string name, TSimpleNameSyntax nameNode, SymbolFilter filter) + string name, TSimpleNameSyntax nameNode, SymbolFilter filter, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); if (name != null && string.IsNullOrWhiteSpace(name)) { return ImmutableArray>.Empty; } using var query = Exact ? SearchQuery.Create(name, ignoreCase: true) : SearchQuery.CreateFuzzy(name); - var symbols = await FindDeclarationsAsync(filter, query).ConfigureAwait(false); + var symbols = await FindDeclarationsAsync(filter, query, cancellationToken).ConfigureAwait(false); if (Exact) { diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs index 022ecd693b905..247639a34b401 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs @@ -26,17 +26,17 @@ private class SourceSymbolsProjectSearchScope : ProjectSearchScope public SourceSymbolsProjectSearchScope( AbstractAddImportFeatureService provider, ConcurrentDictionary> projectToAssembly, - Project project, bool ignoreCase, CancellationToken cancellationToken) - : base(provider, project, ignoreCase, cancellationToken) + Project project, bool ignoreCase) + : base(provider, project, ignoreCase) { _projectToAssembly = projectToAssembly; } protected override async Task> FindDeclarationsAsync( - SymbolFilter filter, SearchQuery searchQuery) + SymbolFilter filter, SearchQuery searchQuery, CancellationToken cancellationToken) { var service = _project.Solution.Services.GetRequiredService(); - var info = await service.TryGetPotentiallyStaleSourceSymbolTreeInfoAsync(_project, CancellationToken).ConfigureAwait(false); + var info = await service.TryGetPotentiallyStaleSourceSymbolTreeInfoAsync(_project, cancellationToken).ConfigureAwait(false); if (info == null) { // Looks like there was nothing in the cache. Return no results for now. @@ -49,7 +49,7 @@ private class SourceSymbolsProjectSearchScope : ProjectSearchScope var lazyAssembly = _projectToAssembly.GetOrAdd(_project, CreateLazyAssembly); var declarations = await info.FindAsync( - searchQuery, lazyAssembly, filter, CancellationToken).ConfigureAwait(false); + searchQuery, lazyAssembly, filter, cancellationToken).ConfigureAwait(false); return declarations; @@ -58,7 +58,7 @@ private class SourceSymbolsProjectSearchScope : ProjectSearchScope { var compilation = await project.GetRequiredCompilationAsync(c).ConfigureAwait(false); return compilation.Assembly; - }, cacheResult: true); + }); } } } diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 63f0d03d3f4f6..b2a07f25194ff 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs @@ -87,42 +87,25 @@ private ISet GetNamespacesInScope(CancellationToken cancellati private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol containingNamespace) => _semanticModel.Compilation.GetCompilationNamespace(containingNamespace) ?? containingNamespace; - internal Task> FindInAllSymbolsInStartingProjectAsync( - bool exact, CancellationToken cancellationToken) - { - var searchScope = new AllSymbolsProjectSearchScope( - _owner, _document.Project, exact, cancellationToken); - return DoAsync(searchScope); - } + internal Task> FindInAllSymbolsInStartingProjectAsync(bool exact, CancellationToken cancellationToken) + => DoAsync(new AllSymbolsProjectSearchScope(_owner, _document.Project, exact), cancellationToken); - internal Task> FindInSourceSymbolsInProjectAsync( - ConcurrentDictionary> projectToAssembly, - Project project, bool exact, CancellationToken cancellationToken) - { - var searchScope = new SourceSymbolsProjectSearchScope( - _owner, projectToAssembly, project, exact, cancellationToken); - return DoAsync(searchScope); - } + internal Task> FindInSourceSymbolsInProjectAsync(ConcurrentDictionary> projectToAssembly, Project project, bool exact, CancellationToken cancellationToken) + => DoAsync(new SourceSymbolsProjectSearchScope(_owner, projectToAssembly, project, exact), cancellationToken); - internal Task> FindInMetadataSymbolsAsync( - IAssemblySymbol assembly, Project assemblyProject, PortableExecutableReference metadataReference, - bool exact, CancellationToken cancellationToken) - { - var searchScope = new MetadataSymbolsSearchScope( - _owner, assemblyProject, assembly, metadataReference, exact, cancellationToken); - return DoAsync(searchScope); - } + internal Task> FindInMetadataSymbolsAsync(IAssemblySymbol assembly, Project assemblyProject, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) + => DoAsync(new MetadataSymbolsSearchScope(_owner, assemblyProject, assembly, metadataReference, exact), cancellationToken); - private async Task> DoAsync(SearchScope searchScope) + private async Task> DoAsync(SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); // Spin off tasks to do all our searching in parallel using var _1 = ArrayBuilder>>.GetInstance(out var tasks); - tasks.Add(GetReferencesForMatchingTypesAsync(searchScope)); - tasks.Add(GetReferencesForMatchingNamespacesAsync(searchScope)); - tasks.Add(GetReferencesForMatchingFieldsAndPropertiesAsync(searchScope)); - tasks.Add(GetReferencesForMatchingExtensionMethodsAsync(searchScope)); + tasks.Add(GetReferencesForMatchingTypesAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForMatchingNamespacesAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForMatchingFieldsAndPropertiesAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForMatchingExtensionMethodsAsync(searchScope, cancellationToken)); // Searching for things like "Add" (for collection initializers) and "Select" // (for extension methods) should only be done when doing an 'exact' search. @@ -132,16 +115,16 @@ private async Task> DoAsync(SearchScope searchSc // query expression valid. if (searchScope.Exact) { - tasks.Add(GetReferencesForCollectionInitializerMethodsAsync(searchScope)); - tasks.Add(GetReferencesForQueryPatternsAsync(searchScope)); - tasks.Add(GetReferencesForDeconstructAsync(searchScope)); - tasks.Add(GetReferencesForGetAwaiterAsync(searchScope)); - tasks.Add(GetReferencesForGetEnumeratorAsync(searchScope)); - tasks.Add(GetReferencesForGetAsyncEnumeratorAsync(searchScope)); + tasks.Add(GetReferencesForCollectionInitializerMethodsAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForQueryPatternsAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForDeconstructAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForGetAwaiterAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForGetEnumeratorAsync(searchScope, cancellationToken)); + tasks.Add(GetReferencesForGetAsyncEnumeratorAsync(searchScope, cancellationToken)); } await Task.WhenAll(tasks).ConfigureAwait(false); - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); using var _2 = ArrayBuilder.GetInstance(out var allReferences); foreach (var task in tasks) @@ -180,9 +163,10 @@ private ImmutableArray DeDupeAndSortReferences(ImmutableArrays or s those types are /// contained in. /// - private async Task> GetReferencesForMatchingTypesAsync(SearchScope searchScope) + private async Task> GetReferencesForMatchingTypesAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (!_owner.CanAddImportForType(_diagnosticId, _node, out var nameNode)) { return ImmutableArray.Empty; @@ -193,18 +177,19 @@ private async Task> GetReferencesForMatchingType out var name, out var arity, out var inAttributeContext, out var hasIncompleteParentMember, out var looksGeneric); - if (ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken: searchScope.CancellationToken)) + if (ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken: cancellationToken)) { // If the expression bound, there's nothing to do. return ImmutableArray.Empty; } - var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Type).ConfigureAwait(false); + var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); // also lookup type symbols with the "Attribute" suffix if necessary. if (inAttributeContext) { - var attributeSymbols = await searchScope.FindDeclarationsAsync(name + AttributeSuffix, nameNode, SymbolFilter.Type).ConfigureAwait(false); + var attributeSymbols = await searchScope.FindDeclarationsAsync( + name + AttributeSuffix, nameNode, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); symbols = symbols.AddRange( attributeSymbols.Select(r => r.WithDesiredName(r.DesiredName.GetWithoutAttributeSuffix(isCaseSensitive: false)))); @@ -266,17 +251,17 @@ private async Task> GetReferencesForMatchingType /// to the s those namespaces are contained in. /// private async Task> GetReferencesForMatchingNamespacesAsync( - SearchScope searchScope) + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForNamespace(_diagnosticId, _node, out var nameNode)) { _syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out var arity); if (arity == 0 && - !ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken: searchScope.CancellationToken)) + !ExpressionBinds(nameNode, checkForExtensionMethods: false, cancellationToken)) { - var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Namespace).ConfigureAwait(false); + var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Namespace, cancellationToken).ConfigureAwait(false); var namespaceSymbols = OfType(symbols); var containingNamespaceSymbols = OfType(symbols).SelectAsArray(s => s.WithSymbol(s.Symbol.ContainingNamespace)); @@ -293,9 +278,9 @@ private async Task> GetReferencesForMatchingType /// containing 'Color' as if we import them it can resolve this issue. /// private async Task> GetReferencesForMatchingFieldsAndPropertiesAsync( - SearchScope searchScope) + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode) && nameNode != null) { @@ -312,7 +297,7 @@ private async Task> GetReferencesForMatchingType if (expression is TSimpleNameSyntax simpleName) { // Check if the expression before the dot binds to a property or field. - var symbol = _semanticModel.GetSymbolInfo(expression, searchScope.CancellationToken).GetAnySymbol(); + var symbol = _semanticModel.GetSymbolInfo(expression, cancellationToken).GetAnySymbol(); if (symbol?.Kind is SymbolKind.Property or SymbolKind.Field) { // Check if we have the 'Color Color' case. @@ -322,7 +307,7 @@ private async Task> GetReferencesForMatchingType { // Try to look up 'Color' as a type. var symbolResults = await searchScope.FindDeclarationsAsync( - symbol.Name, simpleName, SymbolFilter.Type).ConfigureAwait(false); + symbol.Name, simpleName, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); // Return results that have accessible members. var namedTypeSymbols = OfType(symbolResults); @@ -354,26 +339,27 @@ private bool HasAccessibleStaticFieldOrProperty(INamedTypeSymbol namedType, stri /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForMatchingExtensionMethodsAsync(SearchScope searchScope) + private async Task> GetReferencesForMatchingExtensionMethodsAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode) && nameNode != null) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); // See if the name binds. If it does, there's nothing further we need to do. - if (!ExpressionBinds(nameNode, checkForExtensionMethods: true, cancellationToken: searchScope.CancellationToken)) + if (!ExpressionBinds(nameNode, checkForExtensionMethods: true, cancellationToken)) { _syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out var arity); if (name != null) { - var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Member).ConfigureAwait(false); + var symbols = await searchScope.FindDeclarationsAsync(name, nameNode, SymbolFilter.Member, cancellationToken).ConfigureAwait(false); var methodSymbols = OfType(symbols); var extensionMethodSymbols = GetViableExtensionMethods( - methodSymbols, nameNode.Parent, searchScope.CancellationToken); + methodSymbols, nameNode.Parent, cancellationToken); var namespaceSymbols = extensionMethodSymbols.SelectAsArray(s => s.WithSymbol(s.Symbol.ContainingNamespace)); return GetNamespaceSymbolReferences(searchScope, namespaceSymbols); @@ -412,15 +398,16 @@ private async Task> GetReferencesForMatchingExte /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForCollectionInitializerMethodsAsync(SearchScope searchScope) + private async Task> GetReferencesForCollectionInitializerMethodsAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out _) && !_syntaxFacts.IsSimpleName(_node) && _owner.IsAddMethodContext(_node, _semanticModel)) { var symbols = await searchScope.FindDeclarationsAsync( - nameof(IList.Add), nameNode: null, filter: SymbolFilter.Member).ConfigureAwait(false); + nameof(IList.Add), nameNode: null, filter: SymbolFilter.Member, cancellationToken).ConfigureAwait(false); // Note: there is no desiredName for these search results. We're searching for // extension methods called "Add", but we have no intention of renaming any @@ -428,7 +415,7 @@ private async Task> GetReferencesForCollectionIn var methodSymbols = OfType(symbols).SelectAsArray(s => s.WithDesiredName(null)); var viableMethods = GetViableExtensionMethods( - methodSymbols, _node.Parent, searchScope.CancellationToken); + methodSymbols, _node.Parent, cancellationToken); return GetNamespaceSymbolReferences(searchScope, viableMethods.SelectAsArray(m => m.WithSymbol(m.Symbol.ContainingNamespace))); @@ -442,18 +429,19 @@ private async Task> GetReferencesForCollectionIn /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForQueryPatternsAsync(SearchScope searchScope) + private async Task> GetReferencesForQueryPatternsAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForQuery(_diagnosticId, _node)) { - var type = _owner.GetQueryClauseInfo(_semanticModel, _node, searchScope.CancellationToken); + var type = _owner.GetQueryClauseInfo(_semanticModel, _node, cancellationToken); if (type != null) { // find extension methods named "Select" return await GetReferencesForExtensionMethodAsync( - searchScope, nameof(Enumerable.Select), type).ConfigureAwait(false); + searchScope, nameof(Enumerable.Select), type, predicate: null, cancellationToken).ConfigureAwait(false); } } @@ -465,17 +453,20 @@ private async Task> GetReferencesForQueryPattern /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForGetAwaiterAsync(SearchScope searchScope) + private async Task> GetReferencesForGetAwaiterAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForGetAwaiter(_diagnosticId, _syntaxFacts, _node)) { var type = GetAwaitInfo(_semanticModel, _syntaxFacts, _node); if (type != null) { - return await GetReferencesForExtensionMethodAsync(searchScope, WellKnownMemberNames.GetAwaiter, type, - m => m.IsValidGetAwaiter()).ConfigureAwait(false); + return await GetReferencesForExtensionMethodAsync( + searchScope, WellKnownMemberNames.GetAwaiter, type, + static m => m.IsValidGetAwaiter(), + cancellationToken).ConfigureAwait(false); } } @@ -487,17 +478,20 @@ private async Task> GetReferencesForGetAwaiterAs /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForGetEnumeratorAsync(SearchScope searchScope) + private async Task> GetReferencesForGetEnumeratorAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForGetEnumerator(_diagnosticId, _syntaxFacts, _node)) { var type = GetCollectionExpressionType(_semanticModel, _syntaxFacts, _node); if (type != null) { - return await GetReferencesForExtensionMethodAsync(searchScope, WellKnownMemberNames.GetEnumeratorMethodName, type, - m => m.IsValidGetEnumerator()).ConfigureAwait(false); + return await GetReferencesForExtensionMethodAsync( + searchScope, WellKnownMemberNames.GetEnumeratorMethodName, type, + static m => m.IsValidGetEnumerator(), + cancellationToken).ConfigureAwait(false); } } @@ -509,17 +503,20 @@ private async Task> GetReferencesForGetEnumerato /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForGetAsyncEnumeratorAsync(SearchScope searchScope) + private async Task> GetReferencesForGetAsyncEnumeratorAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForGetAsyncEnumerator(_diagnosticId, _syntaxFacts, _node)) { var type = GetCollectionExpressionType(_semanticModel, _syntaxFacts, _node); if (type != null) { - return await GetReferencesForExtensionMethodAsync(searchScope, WellKnownMemberNames.GetAsyncEnumeratorMethodName, type, - m => m.IsValidGetAsyncEnumerator()).ConfigureAwait(false); + return await GetReferencesForExtensionMethodAsync( + searchScope, WellKnownMemberNames.GetAsyncEnumeratorMethodName, type, + static m => m.IsValidGetAsyncEnumerator(), + cancellationToken).ConfigureAwait(false); } } @@ -531,13 +528,14 @@ private async Task> GetReferencesForGetAsyncEnum /// s to the s that contain /// the static classes that those extension methods are contained in. /// - private async Task> GetReferencesForDeconstructAsync(SearchScope searchScope) + private async Task> GetReferencesForDeconstructAsync( + SearchScope searchScope, CancellationToken cancellationToken) { - searchScope.CancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); if (_owner.CanAddImportForDeconstruct(_diagnosticId, _node)) { - var type = _owner.GetDeconstructInfo(_semanticModel, _node, searchScope.CancellationToken); + var type = _owner.GetDeconstructInfo(_semanticModel, _node, cancellationToken); if (type != null) { // Note: we could check that the extension methods have the right number of out-params. @@ -545,8 +543,7 @@ private async Task> GetReferencesForDeconstructA // we'll just be permissive, with the assumption that there won't be that many matching // 'Deconstruct' extension methods for the type of node that we're on. return await GetReferencesForExtensionMethodAsync( - searchScope, "Deconstruct", type, - m => m.ReturnsVoid).ConfigureAwait(false); + searchScope, "Deconstruct", type, static m => m.ReturnsVoid, cancellationToken).ConfigureAwait(false); } } @@ -554,10 +551,10 @@ private async Task> GetReferencesForDeconstructA } private async Task> GetReferencesForExtensionMethodAsync( - SearchScope searchScope, string name, ITypeSymbol type, Func predicate = null) + SearchScope searchScope, string name, ITypeSymbol type, Func predicate, CancellationToken cancellationToken) { var symbols = await searchScope.FindDeclarationsAsync( - name, nameNode: null, filter: SymbolFilter.Member).ConfigureAwait(false); + name, nameNode: null, filter: SymbolFilter.Member, cancellationToken).ConfigureAwait(false); // Note: there is no "desiredName" when doing this. We're not going to do any // renames of the user code. We're just looking for an extension method called diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs index af318c6558c01..a1284d2bf2ac5 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; @@ -17,15 +18,13 @@ internal abstract partial class AbstractAddImportFeatureService allReferences, CancellationToken cancellationToken) + ConcurrentQueue allReferences, CancellationToken cancellationToken) { + // Only do this if none of the project or metadata searches produced + // any results. We always consider source and local metadata to be + // better than any NuGet/assembly-reference results. if (allReferences.Count > 0) - { - // Only do this if none of the project or metadata searches produced - // any results. We always consider source and local metadata to be - // better than any NuGet/assembly-reference results. return; - } if (!_owner.CanAddImportForType(_diagnosticId, _node, out var nameNode)) { @@ -46,7 +45,7 @@ private partial class SymbolReferenceFinder } private async Task FindNugetOrReferenceAssemblyTypeReferencesAsync( - ArrayBuilder allReferences, TSimpleNameSyntax nameNode, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, bool inAttributeContext, CancellationToken cancellationToken) { @@ -63,7 +62,7 @@ private partial class SymbolReferenceFinder } private async Task FindNugetOrReferenceAssemblyTypeReferencesWorkerAsync( - ArrayBuilder allReferences, TSimpleNameSyntax nameNode, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, bool isAttributeSearch, CancellationToken cancellationToken) { if (_options.SearchOptions.SearchReferenceAssemblies) @@ -84,7 +83,7 @@ private partial class SymbolReferenceFinder } private async Task FindReferenceAssemblyTypeReferencesAsync( - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, @@ -110,7 +109,7 @@ private partial class SymbolReferenceFinder private async Task FindNugetTypeReferencesAsync( string sourceName, string sourceUrl, - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, @@ -132,7 +131,7 @@ private partial class SymbolReferenceFinder } private async Task HandleReferenceAssemblyReferenceAsync( - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, Project project, bool isAttributeSearch, @@ -154,20 +153,20 @@ private partial class SymbolReferenceFinder } var desiredName = GetDesiredName(isAttributeSearch, result.TypeName); - allReferences.Add(new AssemblyReference( + allReferences.Enqueue(new AssemblyReference( _owner, new SearchResult(desiredName, nameNode, result.ContainingNamespaceNames.ToReadOnlyList(), weight), result)); } private void HandleNugetReference( string source, - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, bool isAttributeSearch, PackageWithTypeResult result, int weight) { var desiredName = GetDesiredName(isAttributeSearch, result.TypeName); - allReferences.Add(new PackageReference(_owner, + allReferences.Enqueue(new PackageReference(_owner, new SearchResult(desiredName, nameNode, result.ContainingNamespaceNames.ToReadOnlyList(), weight), source, result.PackageName, result.Version)); } diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index 9fe527b3028cf..b02b3f238ba34 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -67,9 +67,11 @@ internal abstract class AbstractChangeSignatureService : ILanguageService /// For some Foo(int x, params int[] p), this helps convert the "1, 2, 3" in Foo(0, 1, 2, 3) /// to "new int[] { 1, 2, 3 }" in Foo(0, new int[] { 1, 2, 3 }); /// - protected abstract SyntaxNode CreateExplicitParamsArrayFromIndividualArguments(SeparatedSyntaxList newArguments, int startingIndex, IParameterSymbol parameterSymbol); + protected abstract TArgumentSyntax CreateExplicitParamsArrayFromIndividualArguments(SeparatedSyntaxList newArguments, int startingIndex, IParameterSymbol parameterSymbol) + where TArgumentSyntax : SyntaxNode; - protected abstract SyntaxNode AddNameToArgument(SyntaxNode argument, string name); + protected abstract TArgumentSyntax AddNameToArgument(TArgumentSyntax argument, string name) + where TArgumentSyntax : SyntaxNode; /// /// Only some languages support: @@ -750,9 +752,9 @@ protected static int GetParameterIndex(SeparatedSyntaxList paramet return separators.ToImmutable(); } - protected virtual async Task> AddNewArgumentsToListAsync( + protected virtual async Task> AddNewArgumentsToListAsync( ISymbol declarationSymbol, - SeparatedSyntaxList newArguments, + SeparatedSyntaxList newArguments, SignatureChange signaturePermutation, bool isReducedExtensionMethod, bool isParamsArrayExpanded, @@ -760,8 +762,9 @@ protected static int GetParameterIndex(SeparatedSyntaxList paramet Document document, int position, CancellationToken cancellationToken) + where TArgumentSyntax : SyntaxNode { - var fullList = ArrayBuilder.GetInstance(); + var fullList = ArrayBuilder.GetInstance(); var separators = ArrayBuilder.GetInstance(); var updatedParameters = signaturePermutation.UpdatedConfiguration.ToListOfParameters(); @@ -815,10 +818,10 @@ protected static int GetParameterIndex(SeparatedSyntaxList paramet // TODO: Need to be able to specify which kind of attribute argument it is to the SyntaxGenerator. // https://github.com/dotnet/roslyn/issues/43354 var argument = generateAttributeArguments - ? Generator.AttributeArgument( + ? (TArgumentSyntax)Generator.AttributeArgument( name: seenNamedArguments || addedParameter.CallSiteKind == CallSiteKind.ValueWithName ? addedParameter.Name : null, expression: expression) - : Generator.Argument( + : (TArgumentSyntax)Generator.Argument( name: seenNamedArguments || addedParameter.CallSiteKind == CallSiteKind.ValueWithName ? addedParameter.Name : null, refKind: RefKind.None, expression: expression); diff --git a/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs b/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs new file mode 100644 index 0000000000000..162718b770f50 --- /dev/null +++ b/src/Features/Core/Portable/CodeFixesAndRefactorings/CodeActionRequestPriorityProvider.cs @@ -0,0 +1,129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeActions +{ + internal interface ICodeActionRequestPriorityProvider + { + CodeActionRequestPriority Priority { get; } + + /// + /// Tracks the given as a de-prioritized analyzer that should be moved to + /// bucket. + /// + void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer); + + bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer); + } + + internal static class ICodeActionRequestPriorityProviderExtensions + { + /// + /// Returns true if the given can report diagnostics that can have fixes from a code + /// fix provider with matching . This method is useful for performing a performance + /// optimization for lightbulb diagnostic computation, wherein we can reduce the set of analyzers to be executed + /// when computing fixes for a specific . + /// + public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, DiagnosticAnalyzer analyzer) + { + var priority = provider.Priority; + + // If caller isn't asking for prioritized result, then run all analyzers. + if (priority == CodeActionRequestPriority.None) + return true; + + // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, + // which requires all analyzer diagnostics. + if (priority == CodeActionRequestPriority.Lowest) + return true; + + // The compiler analyzer always counts for any priority. It's diagnostics may be fixed + // by high pri or normal pri fixers. + if (analyzer.IsCompilerAnalyzer()) + return true; + + // Check if we are computing diagnostics for 'CodeActionRequestPriority.Low' and + // this analyzer was de-prioritized to low priority bucket. + if (priority == CodeActionRequestPriority.Low && + provider.IsDeprioritizedAnalyzerWithLowPriority(analyzer)) + { + return true; + } + + // Now compute this analyzer's priority and compare it with the provider's request 'Priority'. + // Our internal 'IBuiltInAnalyzer' can specify custom request priority, while all + // the third-party analyzers are assigned 'Normal' priority. + var analyzerPriority = analyzer is IBuiltInAnalyzer { RequestPriority: var requestPriority } + ? requestPriority + : CodeActionRequestPriority.Normal; + + return priority == analyzerPriority; + } + + /// + /// Returns true if the given should be considered a candidate when computing + /// fixes for the given . + /// + public static bool MatchesPriority(this ICodeActionRequestPriorityProvider provider, CodeFixProvider codeFixProvider) + { + if (provider.Priority == CodeActionRequestPriority.None) + { + // We are computing fixes for all priorities + return true; + } + if (provider.Priority == CodeActionRequestPriority.Low) + { + // 'Low' priority can be used for two types of code fixers: + // 1. Those which explicitly set their 'RequestPriority' to 'Low' and + // 2. Those which can fix diagnostics for expensive analyzers which were de-prioritized + // to 'Low' priority bucket to improve lightbulb population performance. + // Hence, when processing the 'Low' Priority bucket, we accept fixers with any RequestPriority, + // as long as they can fix a diagnostic from an analyzer that was executed in the 'Low' bucket. + return true; + } + + return provider.Priority == codeFixProvider.RequestPriority; + } + } + + internal sealed class DefaultCodeActionRequestPriorityProvider : ICodeActionRequestPriorityProvider + { + private readonly object _gate = new(); + private HashSet? _lowPriorityAnalyzers; + + public DefaultCodeActionRequestPriorityProvider(CodeActionRequestPriority priority = CodeActionRequestPriority.None) + { + Priority = priority; + } + + public CodeActionRequestPriority Priority { get; } + + public void AddDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) + { + lock (_gate) + { + _lowPriorityAnalyzers ??= new(); + _lowPriorityAnalyzers.Add(analyzer); + } + } + + public bool IsDeprioritizedAnalyzerWithLowPriority(DiagnosticAnalyzer analyzer) + { + lock (_gate) + { + return _lowPriorityAnalyzers != null && _lowPriorityAnalyzers.Contains(analyzer); + } + } + } +} diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index 42745bc8f874e..fd484b78cc17f 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -103,7 +103,7 @@ static ProjectCodeRefactoringProvider.ExtensionInfo GetExtensionInfo(ExportCodeR RefactoringToMetadataMap.TryGetValue(provider, out var providerMetadata); var refactoring = await GetRefactoringFromProviderAsync( - document, state, provider, providerMetadata, extensionManager, options, isBlocking: false, cancellationToken).ConfigureAwait(false); + document, state, provider, providerMetadata, extensionManager, options, cancellationToken).ConfigureAwait(false); if (refactoring != null) { @@ -119,7 +119,6 @@ static ProjectCodeRefactoringProvider.ExtensionInfo GetExtensionInfo(ExportCodeR TextSpan state, CodeActionRequestPriority priority, CodeActionOptionsProvider options, - bool isBlocking, Func addOperationScope, CancellationToken cancellationToken) { @@ -142,7 +141,7 @@ static ProjectCodeRefactoringProvider.ExtensionInfo GetExtensionInfo(ExportCodeR using (RoslynEventSource.LogInformationalBlock(FunctionId.Refactoring_CodeRefactoringService_GetRefactoringsAsync, providerName, cancellationToken)) { return GetRefactoringFromProviderAsync(document, state, provider, providerMetadata, - extensionManager, options, isBlocking, cancellationToken); + extensionManager, options, cancellationToken); } }, cancellationToken)); @@ -160,7 +159,6 @@ static ProjectCodeRefactoringProvider.ExtensionInfo GetExtensionInfo(ExportCodeR CodeChangeProviderMetadata? providerMetadata, IExtensionManager extensionManager, CodeActionOptionsProvider options, - bool isBlocking, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -189,7 +187,6 @@ static ProjectCodeRefactoringProvider.ExtensionInfo GetExtensionInfo(ExportCodeR } }, options, - isBlocking, cancellationToken); var task = provider.ComputeRefactoringsAsync(context) ?? Task.CompletedTask; diff --git a/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs index e545c51f9ea80..3a38556eb2e98 100644 --- a/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringService.cs @@ -15,12 +15,12 @@ internal interface ICodeRefactoringService { Task HasRefactoringsAsync(TextDocument document, TextSpan textSpan, CodeActionOptionsProvider options, CancellationToken cancellationToken); - Task> GetRefactoringsAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptionsProvider options, bool isBlocking, Func addOperationScope, CancellationToken cancellationToken); + Task> GetRefactoringsAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptionsProvider options, Func addOperationScope, CancellationToken cancellationToken); } internal static class ICodeRefactoringServiceExtensions { - public static Task> GetRefactoringsAsync(this ICodeRefactoringService service, TextDocument document, TextSpan state, CodeActionOptionsProvider options, bool isBlocking, CancellationToken cancellationToken) - => service.GetRefactoringsAsync(document, state, CodeActionRequestPriority.None, options, isBlocking, addOperationScope: _ => null, cancellationToken); + public static Task> GetRefactoringsAsync(this ICodeRefactoringService service, TextDocument document, TextSpan state, CodeActionOptionsProvider options, CancellationToken cancellationToken) + => service.GetRefactoringsAsync(document, state, CodeActionRequestPriority.None, options, addOperationScope: _ => null, cancellationToken); } } diff --git a/src/Features/Core/Portable/Completion/CompletionItem.cs b/src/Features/Core/Portable/Completion/CompletionItem.cs index 4230632eabba5..a897d36157bfa 100644 --- a/src/Features/Core/Portable/Completion/CompletionItem.cs +++ b/src/Features/Core/Portable/Completion/CompletionItem.cs @@ -81,6 +81,8 @@ public sealed class CompletionItem : IComparable /// /// The span identifies the text in the document that is used to filter the initial list presented to the user, /// and typically represents the region of the document that will be changed if this item is committed. + /// The latter is not always true because individual provider is free to make more complex changes to the document. + /// If this is the case, the provider should set to true. /// public TextSpan Span { get; internal set; } @@ -101,17 +103,11 @@ public sealed class CompletionItem : IComparable public CompletionItemRules Rules { get; } /// - /// Returns true if this item's text edit requires complex resolution that - /// may impact performance. For example, an edit may be complex if it needs - /// to format or type check the resulting code, or make complex non-local - /// changes to other parts of the file. - /// Complex resolution is used so we only do the minimum amount of work - /// needed to display completion items. It is performed only for the - /// committed item just prior to commit. Thus, it is ideal for any expensive - /// completion work that does not affect the display of the item in the - /// completion list, but is necessary for committing the item. - /// An example of an item type requiring complex resolution is C#/VB - /// override completion. + /// Returns true if this item's text edit requires complex resolution. + /// An edit is considered complex if the span of the change is different from + /// specified by . + /// + /// Example of an item type requiring complex resolution is C#/VB override completion. /// public bool IsComplexTextEdit { get; } diff --git a/src/Features/Core/Portable/Completion/CompletionList.cs b/src/Features/Core/Portable/Completion/CompletionList.cs index b43b63d566e6e..5ae1d557dab9c 100644 --- a/src/Features/Core/Portable/Completion/CompletionList.cs +++ b/src/Features/Core/Portable/Completion/CompletionList.cs @@ -46,6 +46,9 @@ public sealed class CompletionList /// The span identifies the text in the document that is used to filter the initial list /// presented to the user, and typically represents the region of the document that will /// be changed if this item is committed. + /// The latter is not always the case because each provider is free to make more complex changes + /// to the document. If this is the case, must be + /// set to . /// public TextSpan Span { get; } diff --git a/src/Features/Core/Portable/Completion/CompletionService.cs b/src/Features/Core/Portable/Completion/CompletionService.cs index f847351cbc7b4..f1fd2f15a2bb1 100644 --- a/src/Features/Core/Portable/Completion/CompletionService.cs +++ b/src/Features/Core/Portable/Completion/CompletionService.cs @@ -226,6 +226,8 @@ public virtual TextSpan GetDefaultCompletionListSpan(SourceText text, int caretP (document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false); var change = await provider.GetChangeAsync(document, item, commitCharacter, cancellationToken).ConfigureAwait(false); GC.KeepAlive(semanticModel); + + Debug.Assert(item.Span == change.TextChange.Span || item.IsComplexTextEdit); return change; } else diff --git a/src/Features/Core/Portable/Completion/CompletionService_GetCompletions.cs b/src/Features/Core/Portable/Completion/CompletionService_GetCompletions.cs index 6916591754a54..51a93c50fa657 100644 --- a/src/Features/Core/Portable/Completion/CompletionService_GetCompletions.cs +++ b/src/Features/Core/Portable/Completion/CompletionService_GetCompletions.cs @@ -69,7 +69,7 @@ public abstract partial class CompletionService (document, var semanticModel) = await GetDocumentWithFrozenPartialSemanticsAsync(document, cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var defaultItemSpan = GetDefaultCompletionListSpan(text, caretPosition); + var completionListSpan = GetDefaultCompletionListSpan(text, caretPosition); var providers = _providerManager.GetFilteredProviders(document.Project, roles, trigger, options); @@ -91,7 +91,7 @@ public abstract partial class CompletionService // Now, ask all the triggered providers, in parallel, to populate a completion context. // Note: we keep any context with items *or* with a suggested item. var triggeredContexts = await ComputeNonEmptyCompletionContextsAsync( - document, caretPosition, trigger, options, defaultItemSpan, triggeredProviders, sharedContext, cancellationToken).ConfigureAwait(false); + document, caretPosition, trigger, options, completionListSpan, triggeredProviders, sharedContext, cancellationToken).ConfigureAwait(false); // Nothing to do if we didn't even get any regular items back (i.e. 0 items or suggestion item only.) if (!triggeredContexts.Any(static cc => cc.Items.Count > 0)) @@ -101,7 +101,7 @@ public abstract partial class CompletionService // that's all we'll return. var exclusiveContexts = triggeredContexts.Where(t => t.IsExclusive).ToImmutableArray(); if (!exclusiveContexts.IsEmpty) - return MergeAndPruneCompletionLists(exclusiveContexts, defaultItemSpan, options, isExclusive: true); + return MergeAndPruneCompletionLists(exclusiveContexts, options, isExclusive: true); // Great! We had some items. Now we want to see if any of the other providers // would like to augment the completion list. For example, we might trigger @@ -110,7 +110,7 @@ public abstract partial class CompletionService var augmentingProviders = providers.Except(triggeredProviders).ToImmutableArray(); var augmentingContexts = await ComputeNonEmptyCompletionContextsAsync( - document, caretPosition, trigger, options, defaultItemSpan, augmentingProviders, sharedContext, cancellationToken).ConfigureAwait(false); + document, caretPosition, trigger, options, completionListSpan, augmentingProviders, sharedContext, cancellationToken).ConfigureAwait(false); GC.KeepAlive(semanticModel); @@ -120,7 +120,7 @@ public abstract partial class CompletionService var allContexts = triggeredContexts.Concat(augmentingContexts) .Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]); - return MergeAndPruneCompletionLists(allContexts, defaultItemSpan, options, isExclusive: false); + return MergeAndPruneCompletionLists(allContexts, options, isExclusive: false); ImmutableArray GetTriggeredProviders( Document document, ConcatImmutableArray providers, int caretPosition, CompletionOptions options, CompletionTrigger trigger, ImmutableHashSet? roles, SourceText text) @@ -224,7 +224,7 @@ private static bool HasAnyItems(CompletionContext cc) private static async Task> ComputeNonEmptyCompletionContextsAsync( Document document, int caretPosition, CompletionTrigger trigger, - CompletionOptions options, TextSpan defaultItemSpan, + CompletionOptions options, TextSpan completionListSpan, ImmutableArray providers, SharedSyntaxContextsWithSpeculativeModel sharedContext, CancellationToken cancellationToken) @@ -234,7 +234,7 @@ private static bool HasAnyItems(CompletionContext cc) { completionContextTasks.Add(GetContextAsync( provider, document, caretPosition, trigger, - options, defaultItemSpan, sharedContext, cancellationToken)); + options, completionListSpan, sharedContext, cancellationToken)); } var completionContexts = await Task.WhenAll(completionContextTasks).ConfigureAwait(false); @@ -243,14 +243,11 @@ private static bool HasAnyItems(CompletionContext cc) private CompletionList MergeAndPruneCompletionLists( ImmutableArray completionContexts, - TextSpan defaultSpan, in CompletionOptions options, bool isExclusive) { - // See if any contexts changed the completion list span. If so, the first context that - // changed it 'wins' and picks the span that will be used for all items in the completion - // list. If no contexts changed it, then just use the default span provided by the service. - var finalCompletionListSpan = completionContexts.FirstOrDefault(c => c.CompletionListSpan != defaultSpan)?.CompletionListSpan ?? defaultSpan; + Debug.Assert(!completionContexts.IsDefaultOrEmpty); + using var displayNameToItemsMap = new DisplayNameToItemsMap(this); CompletionItem? suggestionModeItem = null; @@ -272,7 +269,7 @@ private static bool HasAnyItems(CompletionContext cc) } return CompletionList.Create( - finalCompletionListSpan, + completionContexts[0].CompletionListSpan, // All contexts have the same completion list span. displayNameToItemsMap.SortToSegmentedList(), GetRules(options), suggestionModeItem, diff --git a/src/Features/Core/Portable/Completion/INotifyCommittingItemCompletionProvider.cs b/src/Features/Core/Portable/Completion/INotifyCommittingItemCompletionProvider.cs index e79da71842fdd..af3342e4068d3 100644 --- a/src/Features/Core/Portable/Completion/INotifyCommittingItemCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/INotifyCommittingItemCompletionProvider.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.Completion { /// - /// Interface to implement if the provider want to sign up for notifacation when one of the items it provided + /// Interface to implement if the provider want to sign up for notification when one of the items it provided /// is being committed by the host, since calling doesn't necessarily /// lead to commission. /// diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs index 88dfa6f02b32e..50c91a90be7fc 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/AbstractImportCompletionProvider.cs @@ -23,7 +23,6 @@ namespace Microsoft.CodeAnalysis.Completion.Providers { internal abstract class AbstractImportCompletionProvider : LSPCompletionProvider, INotifyCommittingItemCompletionProvider { - protected abstract Task> GetImportedNamespacesAsync(SyntaxContext syntaxContext, CancellationToken cancellationToken); protected abstract bool ShouldProvideCompletion(CompletionContext completionContext, SyntaxContext syntaxContext); protected abstract void WarmUpCacheInBackground(Document document); protected abstract Task AddCompletionItemsAsync(CompletionContext completionContext, SyntaxContext syntaxContext, HashSet namespacesInScope, CancellationToken cancellationToken); @@ -63,7 +62,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC // Find all namespaces in scope at current cursor location, // which will be used to filter so the provider only returns out-of-scope types. - var namespacesInScope = await GetNamespacesInScopeAsync(syntaxContext, cancellationToken).ConfigureAwait(false); + var namespacesInScope = GetNamespacesInScope(syntaxContext, cancellationToken); await AddCompletionItemsAsync(completionContext, syntaxContext, namespacesInScope, cancellationToken).ConfigureAwait(false); } @@ -75,12 +74,12 @@ private static async Task CreateContextAsync(Document document, i return document.GetRequiredLanguageService().CreateContext(document, semanticModel, position, cancellationToken); } - private async Task> GetNamespacesInScopeAsync(SyntaxContext syntaxContext, CancellationToken cancellationToken) + private static HashSet GetNamespacesInScope(SyntaxContext syntaxContext, CancellationToken cancellationToken) { var semanticModel = syntaxContext.SemanticModel; var document = syntaxContext.Document; - var importedNamespaces = await GetImportedNamespacesAsync(syntaxContext, cancellationToken).ConfigureAwait(false); + var importedNamespaces = GetImportedNamespaces(syntaxContext, cancellationToken); // This hashset will be used to match namespace names, so it must have the same case-sensitivity as the source language. var syntaxFacts = document.GetRequiredLanguageService(); @@ -97,6 +96,35 @@ private async Task> GetNamespacesInScopeAsync(SyntaxContext synt return namespacesInScope; } + private static ImmutableArray GetImportedNamespaces(SyntaxContext context, CancellationToken cancellationToken) + { + var position = context.Position; + var targetToken = context.TargetToken; + + // If we are immediately after `using` directive adjust position to the start of the next token. + // This is a workaround for an issue, when immediately after a `using` directive it is not included into the import scope. + // See https://github.com/dotnet/roslyn/issues/67447 for more info. + if (context.IsRightAfterUsingOrImportDirective) + position = targetToken.GetNextToken(includeZeroWidth: true).SpanStart; + + var scopes = context.SemanticModel.GetImportScopes(position, cancellationToken); + + using var _ = ArrayBuilder.GetInstance(out var usingsBuilder); + + foreach (var scope in scopes) + { + foreach (var import in scope.Imports) + { + if (import.NamespaceOrType is INamespaceSymbol @namespace) + { + usingsBuilder.Add(@namespace.ToDisplayString(SymbolDisplayFormats.NameFormat)); + } + } + } + + return usingsBuilder.ToImmutable(); + } + public override async Task GetChangeAsync( Document document, CompletionItem completionItem, char? commitKey, CancellationToken cancellationToken) { diff --git a/src/Features/Core/Portable/Completion/Providers/XmlDocCommentCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/XmlDocCommentCompletionItem.cs index c74d5c7242959..d98f6b4cafa5d 100644 --- a/src/Features/Core/Portable/Completion/Providers/XmlDocCommentCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/XmlDocCommentCompletionItem.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.CodeAnalysis.Completion.Providers { @@ -18,12 +17,18 @@ public static CompletionItem Create(string displayText, string beforeCaretText, .Add(BeforeCaretText, beforeCaretText) .Add(AfterCaretText, afterCaretText); + // Set isComplexTextEdit to be always true for simplicity, even + // though we don't always need to make change outside the default + // completion list Span. + // See AbstractDocCommentCompletionProvider.GetChangeAsync for how + // the actual Span is calculated. return CommonCompletionItem.Create( displayText: displayText, displayTextSuffix: "", glyph: Glyph.Keyword, properties: props, - rules: rules); + rules: rules, + isComplexTextEdit: true); } public static string GetBeforeCaretText(CompletionItem item) diff --git a/src/Features/Core/Portable/Completion/SharedSyntaxContextsWithSpeculativeModel.cs b/src/Features/Core/Portable/Completion/SharedSyntaxContextsWithSpeculativeModel.cs index 8d01f369e8508..8da8a3db49e6a 100644 --- a/src/Features/Core/Portable/Completion/SharedSyntaxContextsWithSpeculativeModel.cs +++ b/src/Features/Core/Portable/Completion/SharedSyntaxContextsWithSpeculativeModel.cs @@ -44,7 +44,7 @@ public Task GetSyntaxContextAsync(Document document, Cancellation static AsyncLazy GetLazySyntaxContextWithSpeculativeModel(Document document, SharedSyntaxContextsWithSpeculativeModel self) { return self._cache.GetOrAdd(document, d => AsyncLazy.Create(cancellationToken - => Utilities.CreateSyntaxContextWithExistingSpeculativeModelAsync(d, self._position, cancellationToken), cacheResult: true)); + => Utilities.CreateSyntaxContextWithExistingSpeculativeModelAsync(d, self._position, cancellationToken))); } } } diff --git a/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs index 200fbaa386dd6..5e242ce2dec1e 100644 --- a/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs @@ -116,7 +116,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var collectionVariableToken = generator.Identifier(collectionVariable.ToString()).WithAdditionalAnnotations(RenameAnnotation.Create()); // this expression is from user code. don't simplify this. - var expression = foreachCollectionExpression.WithoutAnnotations(SimplificationHelpers.DontSimplifyAnnotation); + var expression = foreachCollectionExpression.WithoutAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation); var collectionStatement = generator.LocalDeclarationStatement( type, collectionVariableToken, diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs index 9f223f5acc547..a55a0ed7fe36e 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs @@ -149,7 +149,7 @@ async Task<(TInvocationExpressionSyntax? invocation, TArgumentSyntax? placeholde if (invocation != null) { // look for a string argument containing `"...{0}..."`, followed by more arguments. - var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(invocation); + var arguments = (SeparatedSyntaxList)syntaxFacts.GetArgumentsOfInvocationExpression(invocation); for (int i = 0, n = arguments.Count - 1; i < n; i++) { var argument = arguments[i]; @@ -231,7 +231,7 @@ bool ContainsIndex(string stringLiteralText, string indexString) var syntaxFacts = document.GetRequiredLanguageService(); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var arguments = syntaxFacts.GetArgumentsOfInvocationExpression(invocation); + var arguments = (SeparatedSyntaxList)syntaxFacts.GetArgumentsOfInvocationExpression(invocation); var literalExpression = (TLiteralExpressionSyntax?)syntaxFacts.GetExpressionOfArgument(placeholderArgument); Contract.ThrowIfNull(literalExpression); @@ -252,7 +252,7 @@ bool ContainsIndex(string stringLiteralText, string indexString) var newRoot = root.ReplaceNode(invocation, replacementNode.WithTriviaFrom(invocation)); return document.WithSyntaxRoot(newRoot); - ImmutableArray GetReorderedArgumentsAfterPlaceholderArgument() + ImmutableArray GetReorderedArgumentsAfterPlaceholderArgument() { var placeholderIndex = arguments.IndexOf(placeholderArgument); Contract.ThrowIfTrue(placeholderIndex < 0); @@ -285,7 +285,7 @@ ImmutableArray GetReorderedArgumentsAfterPlaceholderArgument() return unnamedArguments.Concat(namedAndUnnamedArguments); } - ImmutableArray ExpandArgumentExpressions(ImmutableArray argumentsAfterPlaceholder) + ImmutableArray ExpandArgumentExpressions(ImmutableArray argumentsAfterPlaceholder) { using var _ = ArrayBuilder.GetInstance(out var builder); foreach (var argument in argumentsAfterPlaceholder) diff --git a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs index ec42c6a055770..ff979cbe1da97 100644 --- a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs +++ b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs @@ -89,7 +89,7 @@ private static async ValueTask HasDesignerCategoryTypeAsync(Project projec var asyncLazy = s_metadataIdToDesignerAttributeInfo.GetValue( metadataId, _ => AsyncLazy.Create(cancellationToken => - ComputeHasDesignerCategoryTypeAsync(solutionServices, solutionKey, peReference, cancellationToken), cacheResult: true)); + ComputeHasDesignerCategoryTypeAsync(solutionServices, solutionKey, peReference, cancellationToken))); return await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); } @@ -159,7 +159,7 @@ private static async ValueTask HasDesignerCategoryTypeAsync(Project projec // The top level project version for this project. We only care if anything top level changes here. // Downstream impact will already happen due to us keying off of the references a project has (which will // change if anything it depends on changes). - var lazyProjectVersion = AsyncLazy.Create(project.GetSemanticVersionAsync, cacheResult: true); + var lazyProjectVersion = AsyncLazy.Create(project.GetSemanticVersionAsync); // Switch to frozen semantics if requested. We don't need to wait on generators to run here as we want to // be lightweight. We'll also continue running in the future. So if any changes to happen that are @@ -222,28 +222,31 @@ private static async ValueTask HasDesignerCategoryTypeAsync(Project projec bool? hasDesignerCategoryType = null; using var _ = ArrayBuilder<(DesignerAttributeData data, VersionStamp version)>.GetInstance(out var results); - foreach (var document in project.Documents) + + // Avoid realizing document instances until needed. + foreach (var documentId in project.DocumentIds) { // If we're only analyzing a specific document, then skip the rest. - if (specificDocument != null && document != specificDocument) + if (specificDocument != null && documentId != specificDocument.Id) continue; // If we don't have a path for this document, we cant proceed with it. // We need that path to inform the project system which file we're referring to. - if (document.FilePath == null) + var filePath = project.State.DocumentStates.GetRequiredState(documentId).FilePath; + if (filePath is null) continue; // If nothing has changed at the top level between the last time we analyzed this document and now, then // no need to analyze again. var projectVersion = await lazyProjectVersion.GetValueAsync(cancellationToken).ConfigureAwait(false); - if (_documentToLastReportedInformation.TryGetValue(document.Id, out var existingInfo) && + if (_documentToLastReportedInformation.TryGetValue(documentId, out var existingInfo) && existingInfo.projectVersion == projectVersion) { continue; } hasDesignerCategoryType ??= await HasDesignerCategoryTypeAsync(project, cancellationToken).ConfigureAwait(false); - var data = await ComputeDesignerAttributeDataAsync(document, hasDesignerCategoryType.Value).ConfigureAwait(false); + var data = await ComputeDesignerAttributeDataAsync(project, documentId, filePath, hasDesignerCategoryType.Value).ConfigureAwait(false); if (data.Category != existingInfo.category) results.Add((data, projectVersion)); } @@ -251,33 +254,34 @@ private static async ValueTask HasDesignerCategoryTypeAsync(Project projec return results.ToImmutable(); async Task ComputeDesignerAttributeDataAsync( - Document document, bool hasDesignerCategoryType) + Project project, DocumentId documentId, string filePath, bool hasDesignerCategoryType) { - Contract.ThrowIfNull(document.FilePath); - // We either haven't computed the designer info, or our data was out of date. We need // So recompute here. Figure out what the current category is, and if that's different // from what we previously stored. var category = await ComputeDesignerAttributeCategoryAsync( - hasDesignerCategoryType, document, cancellationToken).ConfigureAwait(false); + hasDesignerCategoryType, project, documentId, cancellationToken).ConfigureAwait(false); return new DesignerAttributeData { Category = category, - DocumentId = document.Id, - FilePath = document.FilePath, + DocumentId = documentId, + FilePath = filePath, }; } } public static async Task ComputeDesignerAttributeCategoryAsync( - bool hasDesignerCategoryType, Document document, CancellationToken cancellationToken) + bool hasDesignerCategoryType, Project project, DocumentId documentId, CancellationToken cancellationToken) { // simple case. If there's no DesignerCategory type in this compilation, then there's definitely no // designable types. if (!hasDesignerCategoryType) return null; + // Wait to realize the document to avoid unnecessary allocations when indexing documents. + var document = project.GetRequiredDocument(documentId); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs index 94ad6d68a5495..5440da68a0b85 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs @@ -25,47 +25,43 @@ internal interface IDiagnosticAnalyzerService DiagnosticAnalyzerInfoCache AnalyzerInfoCache { get; } /// - /// Re-analyze given projects and documents + /// Re-analyze given projects and documents. If both and are null, + /// then re-analyzes the entire for the given . /// - void Reanalyze(Workspace workspace, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false); + void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority); /// /// Get specific 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. /// - Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default); + Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// 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. /// - Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId = null, DocumentId? documentId = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default); + Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get diagnostics for the given solution. all diagnostics returned should be up-to-date with respect to the given solution. /// - Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default); + Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Force computes diagnostics and raises diagnostic events for the given project or solution. all diagnostics returned should be up-to-date with respect to the given project or solution. /// - Task ForceAnalyzeAsync(Solution solution, Action onProjectAnalyzed, ProjectId? projectId = null, CancellationToken cancellationToken = default); - - /// - /// True if given project has any diagnostics - /// - bool ContainsDiagnostics(Workspace workspace, ProjectId projectId); + Task ForceAnalyzeAsync(Solution solution, Action onProjectAnalyzed, ProjectId? projectId, CancellationToken cancellationToken); /// /// Get diagnostics of the given diagnostic ids from the given solution. all diagnostics returned should be up-to-date with respect to the given solution. /// Note that for project case, this method returns diagnostics from all project documents as well. Use /// if you want to fetch only project diagnostics without source locations. /// - Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default); + Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Get project diagnostics (diagnostics with no source location) of the given diagnostic ids from the given solution. all diagnostics returned should be up-to-date with respect to the given solution. /// Note that this method doesn't return any document diagnostics. Use to also fetch those. /// - Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default); + Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken); /// /// Try to return up to date diagnostics for the given span for the document. @@ -76,16 +72,16 @@ internal interface IDiagnosticAnalyzerService /// This API will only force complete analyzers that support span based analysis, i.e. compiler analyzer and /// s that support . /// For the rest of the analyzers, it will only return diagnostics if the analyzer has already been executed. - /// Use + /// Use /// if you want to force complete all analyzers and get up-to-date diagnostics for all analyzers for the given span. /// Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, - bool includeSuppressedDiagnostics = false, - CodeActionRequestPriority priority = CodeActionRequestPriority.None, - DiagnosticKind diagnosticKind = DiagnosticKind.All, - bool isExplicit = false, - CancellationToken cancellationToken = default); + bool includeSuppressedDiagnostics, + ICodeActionRequestPriorityProvider priorityProvider, + DiagnosticKind diagnosticKind, + bool isExplicit, + CancellationToken cancellationToken); /// /// Return up to date diagnostics for the given span for the document @@ -98,25 +94,42 @@ internal interface IDiagnosticAnalyzerService Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, - bool includeSuppressedDiagnostics = false, - CodeActionRequestPriority priority = CodeActionRequestPriority.None, - Func? addOperationScope = null, - DiagnosticKind diagnosticKind = DiagnosticKind.All, - bool isExplicit = false, - CancellationToken cancellationToken = default); + bool includeSuppressedDiagnostics, + ICodeActionRequestPriorityProvider priorityProvider, + Func? addOperationScope, + DiagnosticKind diagnosticKind, + bool isExplicit, + CancellationToken cancellationToken); } internal static class IDiagnosticAnalyzerServiceExtensions { + /// + /// Return up to date diagnostics for the given for the given . + /// + /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. + /// + /// public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, - TextDocument document, TextSpan range, string? diagnosticId = null, - bool includeSuppressedDiagnostics = false, Func? addOperationScope = null, - DiagnosticKind diagnosticKind = DiagnosticKind.All, bool isExplicit = false, - CancellationToken cancellationToken = default) - => service.GetDiagnosticsForSpanAsync(document, range, diagnosticId, includeSuppressedDiagnostics, CodeActionRequestPriority.None, addOperationScope, diagnosticKind, isExplicit, cancellationToken); + TextDocument document, TextSpan? range, CancellationToken cancellationToken) + => service.GetDiagnosticsForSpanAsync(document, range, DiagnosticKind.All, cancellationToken); /// - /// Return up to date diagnostics for the given span for the document + /// Return up to date diagnostics of the given for the given + /// for the given . + /// + /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. + /// + /// + public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, + TextDocument document, TextSpan? range, DiagnosticKind diagnosticKind, CancellationToken cancellationToken) + => service.GetDiagnosticsForSpanAsync(document, range, + diagnosticId: null, includeSuppressedDiagnostics: false, + priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + addOperationScope: null, diagnosticKind, isExplicit: false, cancellationToken); + + /// + /// Return up to date diagnostics for the given and parameters for the given . /// /// This can be expensive since it is force analyzing diagnostics if it doesn't have up-to-date one yet. If /// is not null, it gets diagnostics only for this given /// public static Task> GetDiagnosticsForSpanAsync(this IDiagnosticAnalyzerService service, - TextDocument document, TextSpan? range, string? diagnosticId = null, - bool includeSuppressedDiagnostics = false, - CodeActionRequestPriority priority = CodeActionRequestPriority.None, - Func? addOperationScope = null, - DiagnosticKind diagnosticKind = DiagnosticKind.All, - bool isExplicit = false, - CancellationToken cancellationToken = default) + TextDocument document, TextSpan? range, string? diagnosticId, + bool includeSuppressedDiagnostics, + ICodeActionRequestPriorityProvider priorityProvider, + Func? addOperationScope, + DiagnosticKind diagnosticKind, + bool isExplicit, + CancellationToken cancellationToken) { Func? shouldIncludeDiagnostic = diagnosticId != null ? id => id == diagnosticId : null; return service.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic, - includeCompilerDiagnostics: true, includeSuppressedDiagnostics, priority, + includeCompilerDiagnostics: true, includeSuppressedDiagnostics, priorityProvider, addOperationScope, diagnosticKind, isExplicit, cancellationToken); } } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 2ea77484145e6..84ec3c0066259 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -205,8 +205,13 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, TextSpan span, out protected abstract SyntaxNode? TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda); + /// + /// Determines if two syntax nodes are the same, disregarding trivia differences. + /// + protected abstract bool AreEquivalentLambdaBodies(SyntaxNode oldLambda, SyntaxNode oldLambdaBody, SyntaxNode newLambda, SyntaxNode newLambdaBody); + protected abstract Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit); - protected abstract Match ComputeBodyMatch(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable>? knownMatches); + protected abstract Match ComputeBodyMatchImpl(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable>? knownMatches); protected abstract Match ComputeTopLevelDeclarationMatch(SyntaxNode oldDeclaration, SyntaxNode newDeclaration); protected abstract IEnumerable GetSyntaxSequenceEdits(ImmutableArray oldNodes, ImmutableArray newNodes); @@ -243,11 +248,6 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, TextSpan span, out protected abstract bool StatementLabelEquals(SyntaxNode node1, SyntaxNode node2); - /// - /// Determines if two syntax nodes are the same, disregarding trivia differences. - /// - protected abstract bool AreEquivalent(SyntaxNode left, SyntaxNode right); - /// /// Returns true if the code emitted for the old active statement part ( of ) /// is the same as the code emitted for the corresponding new active statement part ( of ). @@ -408,13 +408,13 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind protected abstract ushort LineDirectiveSyntaxKind { get; } protected abstract SymbolDisplayFormat ErrorDisplayFormat { get; } protected abstract List GetExceptionHandlingAncestors(SyntaxNode node, bool isNonLeaf); - protected abstract void GetStateMachineInfo(SyntaxNode body, out ImmutableArray suspensionPoints, out StateMachineKinds kinds); + internal abstract StateMachineInfo GetStateMachineInfo(SyntaxNode body); protected abstract TextSpan GetExceptionHandlingRegion(SyntaxNode node, out bool coversAllChildren); internal abstract void ReportTopLevelSyntacticRudeEdits(ArrayBuilder diagnostics, Match match, Edit edit, Dictionary editMap); internal abstract void ReportEnclosingExceptionHandlingRudeEdits(ArrayBuilder diagnostics, IEnumerable> exceptionHandlingEdits, SyntaxNode oldStatement, TextSpan newStatementSpan); internal abstract void ReportOtherRudeEditsAroundActiveStatement(ArrayBuilder diagnostics, Match match, SyntaxNode oldStatement, SyntaxNode newStatement, bool isNonLeaf); - internal abstract void ReportMemberBodyUpdateRudeEdits(ArrayBuilder diagnostics, SyntaxNode newMember, TextSpan? span); + internal abstract void ReportMemberOrLambdaBodyUpdateRudeEditsImpl(ArrayBuilder diagnostics, SyntaxNode newDeclaration, SyntaxNode newBody, TextSpan? span); internal abstract void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder diagnostics, ISymbol newSymbol, SyntaxNode newNode, bool insertingIntoExistingContainingType); internal abstract void ReportStateMachineSuspensionPointRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode); @@ -428,6 +428,7 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind internal abstract bool IsNestedFunction(SyntaxNode node); internal abstract bool IsLocalFunction(SyntaxNode node); + internal abstract bool IsGenericLocalFunction(SyntaxNode node); internal abstract bool IsClosureScope(SyntaxNode node); internal abstract bool ContainsLambda(SyntaxNode declaration); internal abstract SyntaxNode GetLambda(SyntaxNode lambdaBody); @@ -935,6 +936,9 @@ private LambdaInfo(List? activeNodeIndices, Match? match, Synta NewBody = newLambdaBody; } + public bool HasActiveStatement + => ActiveNodeIndices != null; + public LambdaInfo WithMatch(Match match, SyntaxNode newLambdaBody) => new(ActiveNodeIndices, match, newLambdaBody); } @@ -946,8 +950,8 @@ public LambdaInfo WithMatch(Match match, SyntaxNode newLambdaBody) SyntaxNode? newBody, SemanticModel oldModel, SemanticModel newModel, - ISymbol oldSymbol, - ISymbol newSymbol, + ISymbol oldMember, + ISymbol newMember, SourceText newText, ImmutableArray oldActiveStatements, ImmutableArray newActiveStatementSpans, @@ -992,8 +996,6 @@ public LambdaInfo WithMatch(Match match, SyntaxNode newLambdaBody) try { - ReportMemberBodyUpdateRudeEdits(diagnostics, newDeclaration, GetDiagnosticSpan(newDeclaration, EditKind.Update)); - _testFaultInjector?.Invoke(newBody); // Populated with active lambdas and matched lambdas. @@ -1042,34 +1044,36 @@ public LambdaInfo WithMatch(Match match, SyntaxNode newLambdaBody) activeNodes.Add(new ActiveNode(activeStatementIndex, oldStatementSyntax, oldEnclosingLambdaBody, statementPart, trackedNode)); } - var bodyMatch = ComputeBodyMatch(oldBody, newBody, activeNodes.Where(n => n.EnclosingLambdaBody == null).ToArray(), diagnostics, out var oldHasStateMachineSuspensionPoint, out var newHasStateMachineSuspensionPoint); - var map = ComputeMap(bodyMatch, activeNodes, ref lazyActiveOrMatchedLambdas, diagnostics); + var activeNodesInBody = activeNodes.Where(n => n.EnclosingLambdaBody == null).ToArray(); - if (oldHasStateMachineSuspensionPoint) - { - ReportStateMachineRudeEdits(oldModel.Compilation, oldSymbol, newBody, diagnostics); - } - else if (newHasStateMachineSuspensionPoint && - !capabilities.Grant(EditAndContinueCapabilities.NewTypeDefinition)) - { - // Adding a state machine, either for async or iterator, will require creating a new helper class - // so is a rude edit if the runtime doesn't support it - if (newSymbol is IMethodSymbol { IsAsync: true }) - { - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.MakeMethodAsyncNotSupportedByRuntime, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); - } - else - { - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.MakeMethodIteratorNotSupportedByRuntime, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); - } - } + var bodyMatch = ComputeBodyMatch(oldBody, newBody, activeNodesInBody); + var map = ComputeMap(bodyMatch, activeNodes, ref lazyActiveOrMatchedLambdas); + + var oldStateMachineInfo = GetStateMachineInfo(oldBody); + var newStateMachineInfo = GetStateMachineInfo(newBody); + ReportStateMachineBodyUpdateRudeEdits(bodyMatch, oldStateMachineInfo, newBody, newStateMachineInfo, hasActiveStatement: activeNodesInBody.Length != 0, diagnostics); + + ReportMemberOrLambdaBodyUpdateRudeEdits( + diagnostics, + oldModel, + oldBody, + oldMember, + newDeclaration, + newBody, + newMember, + bodyMatch, + capabilities, + oldStateMachineInfo, + newStateMachineInfo); ReportLambdaAndClosureRudeEdits( oldModel, + oldMember, oldBody, newModel, newBody, - newSymbol, + newMember, + bodyMatch, lazyActiveOrMatchedLambdas, map, capabilities, @@ -1089,7 +1093,7 @@ public LambdaInfo WithMatch(Match match, SyntaxNode newLambdaBody) // We create syntax map even if it's not necessary: if any data member initializers are active/contain lambdas. // Since initializers are usually simple the map should not be large enough to make it worth optimizing it away. if (!activeNodes.IsEmpty() || - newHasStateMachineSuspensionPoint || + newStateMachineInfo.HasSuspensionPoints || newBodyHasLambdas || IsConstructorWithMemberInitializers(newDeclaration) || IsDeclarationWithInitializer(oldDeclaration) || @@ -1350,17 +1354,16 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt } /// - /// Calculates a syntax map of the entire method body including all lambda bodies it contains (recursively). + /// Calculates a syntax map of the entire method body including all lambda bodies it contains. /// private BidirectionalMap ComputeMap( - Match bodyMatch, - ArrayBuilder activeNodes, - ref Dictionary? lazyActiveOrMatchedLambdas, - ArrayBuilder diagnostics) + Match memberBodyMatch, + ArrayBuilder memberBodyActiveNodes, + ref Dictionary? lazyActiveOrMatchedLambdas) { ArrayBuilder>? lambdaBodyMatches = null; var currentLambdaBodyMatch = -1; - var currentBodyMatch = bodyMatch; + var currentBodyMatch = memberBodyMatch; while (true) { @@ -1381,7 +1384,7 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt var newLambdaBody1 = TryGetPartnerLambdaBody(oldLambdaBody1, newNode); if (newLambdaBody1 != null) { - lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody1, newLambdaBody1, activeNodes, lazyActiveOrMatchedLambdas, diagnostics)); + lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody1, newLambdaBody1, memberBodyActiveNodes, lazyActiveOrMatchedLambdas)); } if (oldLambdaBody2 != null) @@ -1389,7 +1392,7 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt var newLambdaBody2 = TryGetPartnerLambdaBody(oldLambdaBody2, newNode); if (newLambdaBody2 != null) { - lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody2, newLambdaBody2, activeNodes, lazyActiveOrMatchedLambdas, diagnostics)); + lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody2, newLambdaBody2, memberBodyActiveNodes, lazyActiveOrMatchedLambdas)); } } } @@ -1406,15 +1409,15 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt if (lambdaBodyMatches == null) { - return BidirectionalMap.FromMatch(bodyMatch); + return BidirectionalMap.FromMatch(memberBodyMatch); } var map = new Dictionary(); var reverseMap = new Dictionary(); // include all matches, including the root: - map.AddRange(bodyMatch.Matches); - reverseMap.AddRange(bodyMatch.ReverseMatches); + map.AddRange(memberBodyMatch.Matches); + reverseMap.AddRange(memberBodyMatch.ReverseMatches); foreach (var lambdaBodyMatch in lambdaBodyMatches) { @@ -1436,46 +1439,43 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt private Match ComputeLambdaBodyMatch( SyntaxNode oldLambdaBody, SyntaxNode newLambdaBody, - IReadOnlyList activeNodes, - [Out] Dictionary activeOrMatchedLambdas, - [Out] ArrayBuilder diagnostics) + IReadOnlyList memberBodyActiveNodes, + [Out] Dictionary activeOrMatchedLambdas) { - ActiveNode[]? activeNodesInLambda; + IEnumerable activeNodesInLambdaBody; if (activeOrMatchedLambdas.TryGetValue(oldLambdaBody, out var info)) { // Lambda may be matched but not be active. - activeNodesInLambda = info.ActiveNodeIndices?.Select(i => activeNodes[i]).ToArray(); + activeNodesInLambdaBody = info.ActiveNodeIndices?.Select(i => memberBodyActiveNodes[i]) ?? Array.Empty(); } else { // If the lambda body isn't in the map it doesn't have any active/tracked statements. - activeNodesInLambda = null; + activeNodesInLambdaBody = Array.Empty(); info = new LambdaInfo(); } - var lambdaBodyMatch = ComputeBodyMatch(oldLambdaBody, - newLambdaBody, activeNodesInLambda ?? Array.Empty(), - diagnostics, out _, out _); + var lambdaBodyMatch = ComputeBodyMatch(oldLambdaBody, newLambdaBody, activeNodesInLambdaBody); activeOrMatchedLambdas[oldLambdaBody] = info.WithMatch(lambdaBodyMatch, newLambdaBody); return lambdaBodyMatch; } - private Match ComputeBodyMatch( - SyntaxNode oldBody, + /// + /// Called for a member body and for bodies of all lambdas and local functions (recursively) found in the member body. + /// + internal Match ComputeBodyMatch(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable activeNodes) + => ComputeBodyMatchImpl(oldBody, newBody, knownMatches: GetMatchingActiveNodes(activeNodes)); + + private void ReportStateMachineBodyUpdateRudeEdits( + Match match, + StateMachineInfo oldStateMachineInfo, SyntaxNode newBody, - ActiveNode[] activeNodes, - ArrayBuilder diagnostics, - out bool oldHasStateMachineSuspensionPoint, - out bool newHasStateMachineSuspensionPoint) + StateMachineInfo newStateMachineInfo, + bool hasActiveStatement, + ArrayBuilder diagnostics) { - List>? lazyKnownMatches = null; - GetStateMachineInfo(oldBody, out var oldStateMachineSuspensionPoints, out var oldStateMachineKinds); - GetStateMachineInfo(newBody, out var newStateMachineSuspensionPoints, out var newStateMachineKinds); - - AddMatchingActiveNodes(ref lazyKnownMatches, activeNodes); - // Consider following cases: // 1) The new method contains yields/awaits but the old doesn't. // If the method has active statements report rude edits for each inserted yield/await (insert "around" an active statement). @@ -1483,38 +1483,26 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt // Report rude edit since we can't remap IP from MoveNext to the kickoff method. // Note that iterators in VB don't need to contain yield, so this case is not covered by change in number of yields. - oldHasStateMachineSuspensionPoint = oldStateMachineSuspensionPoints.Length > 0; - newHasStateMachineSuspensionPoint = newStateMachineSuspensionPoints.Length > 0; - - var match = ComputeBodyMatch(oldBody, newBody, lazyKnownMatches); - - if (IsLocalFunction(match.OldRoot) && IsLocalFunction(match.NewRoot)) - { - ReportMemberBodyUpdateRudeEdits(diagnostics, match.NewRoot, match.NewRoot.Span); - } - - if (oldStateMachineSuspensionPoints.Length > 0) + if (oldStateMachineInfo.HasSuspensionPoints) { foreach (var (oldNode, newNode) in match.Matches) { ReportStateMachineSuspensionPointRudeEdits(diagnostics, oldNode, newNode); } } - else if (activeNodes.Length > 0) + + // It is allowed to update a regular method to an async method or an iterator. + // The only restriction is a presence of an active statement in the method body + // since the debugger does not support remapping active statements to a different method. + if (hasActiveStatement && oldStateMachineInfo.IsStateMachine != newStateMachineInfo.IsStateMachine) { - // It is allowed to update a regular method to an async method or an iterator. - // The only restriction is a presence of an active statement in the method body - // since the debugger does not support remapping active statements to a different method. - if (oldStateMachineKinds != newStateMachineKinds) - { - diagnostics.Add(new RudeEditDiagnostic( - RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, - GetBodyDiagnosticSpan(newBody, EditKind.Update))); - } + diagnostics.Add(new RudeEditDiagnostic( + RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, + GetBodyDiagnosticSpan(newBody, EditKind.Update))); } // report removing async as rude: - if ((oldStateMachineKinds & StateMachineKinds.Async) != 0 && (newStateMachineKinds & StateMachineKinds.Async) == 0) + if (oldStateMachineInfo.IsAsync && !newStateMachineInfo.IsAsync) { diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.ChangingFromAsynchronousToSynchronous, @@ -1524,7 +1512,7 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt } // VB supports iterator lambdas/methods without yields - if ((oldStateMachineKinds & StateMachineKinds.Iterator) != 0 && (newStateMachineKinds & StateMachineKinds.Iterator) == 0) + if (oldStateMachineInfo.IsIterator && !newStateMachineInfo.IsIterator) { diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.ModifiersUpdate, @@ -1532,8 +1520,6 @@ private ActiveStatement GetActiveStatementWithSpan(UnmappedActiveStatement oldSt newBody, new[] { GetBodyDisplayName(newBody) })); } - - return match; } internal virtual void ReportStateMachineSuspensionPointDeletedRudeEdit(ArrayBuilder diagnostics, Match match, SyntaxNode deletedSuspensionPoint) @@ -1554,9 +1540,11 @@ internal virtual void ReportStateMachineSuspensionPointInsertedRudeEdit(ArrayBui new[] { GetSuspensionPointDisplayName(insertedSuspensionPoint, EditKind.Insert) })); } - private static void AddMatchingActiveNodes(ref List>? lazyKnownMatches, IEnumerable activeNodes) + private static List>? GetMatchingActiveNodes(IEnumerable activeNodes) { // add nodes that are tracked by the editor buffer to known matches: + List>? lazyKnownMatches = null; + foreach (var activeNode in activeNodes) { if (activeNode.NewTrackedNode != null) @@ -1565,6 +1553,8 @@ private static void AddMatchingActiveNodes(ref List s var oldSymbol = (otherContainingNode == oldNode) ? otherContainingSymbol : containingSymbol; var newSymbol = (otherContainingNode == oldNode) ? containingSymbol : otherContainingSymbol; - if (!CanAddNewMember(oldSymbol, capabilities, cancellationToken)) + if (!CanRenameOrChangeSignature(oldSymbol, newSymbol, capabilities, cancellationToken)) { notSupportedByRuntime = true; return false; @@ -3681,6 +3668,55 @@ public int GetHashCode([DisallowNull] SemanticEditInfo obj) => obj.Symbol.GetHashCode(); } + private void ReportMemberOrLambdaBodyUpdateRudeEdits( + ArrayBuilder diagnostics, + SemanticModel? oldModel, + SyntaxNode oldBody, + ISymbol oldMember, + SyntaxNode newDeclaration, + SyntaxNode newBody, + ISymbol newMember, + Match memberBodyMatch, + EditAndContinueCapabilitiesGrantor capabilities, + StateMachineInfo oldStateMachineInfo, + StateMachineInfo newStateMachineInfo) + { + ReportMemberOrLambdaBodyUpdateRudeEditsImpl(diagnostics, newDeclaration, newBody, span: null); + + if (oldStateMachineInfo.IsStateMachine) + { + Contract.ThrowIfNull(oldModel); + ReportMissingStateMachineAttribute(oldModel.Compilation, oldStateMachineInfo, newBody, diagnostics); + } + + if (!oldStateMachineInfo.IsStateMachine && + newStateMachineInfo.IsStateMachine && + !capabilities.Grant(EditAndContinueCapabilities.NewTypeDefinition)) + { + // Adding a state machine, either for async or iterator, will require creating a new helper class + // so is a rude edit if the runtime doesn't support it + var rudeEdit = newStateMachineInfo.IsAsync ? RudeEditKind.MakeMethodAsyncNotSupportedByRuntime : RudeEditKind.MakeMethodIteratorNotSupportedByRuntime; + diagnostics.Add(new RudeEditDiagnostic(rudeEdit, GetDiagnosticSpan(newDeclaration, EditKind.Update))); + } + + if (oldStateMachineInfo.IsStateMachine && newStateMachineInfo.IsStateMachine) + { + if (!capabilities.Grant(EditAndContinueCapabilities.AddInstanceFieldToExistingType)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, GetDiagnosticSpan(newDeclaration, EditKind.Update))); + } + + if ((InGenericContext(oldMember) || + InGenericContext(newMember) || + InGenericLocalContext(oldBody, memberBodyMatch.OldRoot) || + InGenericLocalContext(newBody, memberBodyMatch.NewRoot)) && + !capabilities.Grant(EditAndContinueCapabilities.GenericAddFieldToExistingType)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, GetDiagnosticSpan(newDeclaration, EditKind.Update), newDeclaration, new[] { GetDisplayName(newDeclaration) })); + } + } + } + private void ReportUpdatedSymbolDeclarationRudeEdits( ArrayBuilder diagnostics, ISymbol oldSymbol, @@ -3741,7 +3777,7 @@ public int GetHashCode([DisallowNull] SemanticEditInfo obj) { rudeEdit = RudeEditKind.Renamed; } - else if (!CanAddNewMember(oldSymbol, capabilities, cancellationToken)) + else if (!CanRenameOrChangeSignature(oldSymbol, newSymbol, capabilities, cancellationToken)) { rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; } @@ -3757,7 +3793,7 @@ public int GetHashCode([DisallowNull] SemanticEditInfo obj) { rudeEdit = RudeEditKind.Renamed; } - else if (!CanAddNewMember(oldSymbol, capabilities, cancellationToken)) + else if (!CanRenameOrChangeSignature(oldSymbol, newSymbol, capabilities, cancellationToken)) { rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; } @@ -3773,7 +3809,7 @@ public int GetHashCode([DisallowNull] SemanticEditInfo obj) { rudeEdit = RudeEditKind.Renamed; } - else if (!CanAddNewMember(oldSymbol, capabilities, cancellationToken)) + else if (!CanRenameOrChangeSignature(oldSymbol, newSymbol, capabilities, cancellationToken)) { rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; } @@ -3975,6 +4011,15 @@ public int GetHashCode([DisallowNull] SemanticEditInfo obj) } } + // updating within generic context + if (rudeEdit == RudeEditKind.None && + oldSymbol is not INamedTypeSymbol and not ITypeParameterSymbol and not IParameterSymbol && + (InGenericContext(oldSymbol) || InGenericContext(newSymbol)) && + !capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod)) + { + rudeEdit = RudeEditKind.UpdatingGenericNotSupportedByRuntime; + } + if (rudeEdit != RudeEditKind.None) { ReportUpdateRudeEdit(diagnostics, rudeEdit, oldSymbol, newSymbol, newNode, newCompilation, cancellationToken); @@ -4047,7 +4092,7 @@ private static void AnalyzeBaseTypes(INamedTypeSymbol oldType, INamedTypeSymbol } else if (AllowsDeletion(newParameter.ContainingSymbol)) { - if (CanAddNewMember(newParameter.ContainingSymbol, capabilities, cancellationToken)) + if (CanRenameOrChangeSignature(oldParameter.ContainingSymbol, newParameter.ContainingSymbol, capabilities, cancellationToken)) { hasParameterTypeChange = true; } @@ -4096,7 +4141,7 @@ private void AnalyzeReturnType(IMethodSymbol oldMethod, IMethodSymbol newMethod, } else if (AllowsDeletion(newMethod)) { - if (CanAddNewMember(newMethod, capabilities, cancellationToken)) + if (CanRenameOrChangeSignature(oldMethod, newMethod, capabilities, cancellationToken)) { hasReturnTypeChange = true; } @@ -4126,7 +4171,7 @@ private void AnalyzeReturnType(IEventSymbol oldEvent, IEventSymbol newEvent, Edi } else if (AllowsDeletion(newEvent)) { - if (CanAddNewMember(newEvent, capabilities, cancellationToken)) + if (CanRenameOrChangeSignature(oldEvent, newEvent, capabilities, cancellationToken)) { hasReturnTypeChange = true; } @@ -4156,7 +4201,7 @@ private void AnalyzeReturnType(IPropertySymbol oldProperty, IPropertySymbol newP } else if (AllowsDeletion(newProperty)) { - if (CanAddNewMember(newProperty, capabilities, cancellationToken)) + if (CanRenameOrChangeSignature(oldProperty, newProperty, capabilities, cancellationToken)) { hasReturnTypeChange = true; } @@ -4221,13 +4266,6 @@ private static bool IsExtensionMethodThisParameter(IParameterSymbol parameter) { AddCustomAttributeSemanticEdits(semanticEdits, oldSymbol, newSymbol, topMatch, syntaxMap, hasAttributeChange, hasReturnTypeAttributeChange, cancellationToken); } - - // updating generic methods and types - if (InGenericContext(oldSymbol, out var oldIsGenericMethod) || InGenericContext(newSymbol, out _)) - { - var rudeEdit = oldIsGenericMethod ? RudeEditKind.GenericMethodUpdate : RudeEditKind.GenericTypeUpdate; - ReportUpdateRudeEdit(diagnostics, rudeEdit, newSymbol, newNode, cancellationToken); - } } private static void AddCustomAttributeSemanticEdits( @@ -4372,6 +4410,14 @@ private static void AddDelegateBeginInvokeEdit(ArrayBuilder se return false; } + // Updating type parameter attributes is currently not supported. + if (oldSymbol is ITypeParameterSymbol) + { + var rudeEdit = oldSymbol.ContainingSymbol.Kind == SymbolKind.Method ? RudeEditKind.GenericMethodUpdate : RudeEditKind.GenericTypeUpdate; + ReportUpdateRudeEdit(diagnostics, rudeEdit, oldSymbol, newSymbol, newNode, newCompilation, cancellationToken); + return false; + } + // Even if the runtime supports attribute changes, only attributes stored in the CustomAttributes table are editable foreach (var attributeData in changedAttributes) { @@ -4501,7 +4547,15 @@ static bool IsSecurityAttribute(INamedTypeSymbol namedTypeSymbol) } } - private bool CanAddNewMember(ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities, CancellationToken cancellationToken) + /// + /// Check if the allow us to rename or change signature of a member. + /// Such edit translates to an addition of a new member, an update of any method bodies associated with the old one and marking the member as "deleted". + /// + private bool CanRenameOrChangeSignature(ISymbol oldSymbol, ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities, CancellationToken cancellationToken) + => CanAddNewMemberToExistingType(newSymbol, capabilities, cancellationToken) && + CanUpdateMemberBody(oldSymbol, newSymbol, capabilities); + + private bool CanAddNewMemberToExistingType(ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities, CancellationToken cancellationToken) { var requiredCapabilities = EditAndContinueCapabilities.None; @@ -4515,9 +4569,25 @@ private bool CanAddNewMember(ISymbol newSymbol, EditAndContinueCapabilitiesGrant requiredCapabilities |= newSymbol.IsStatic ? EditAndContinueCapabilities.AddStaticFieldToExistingType : EditAndContinueCapabilities.AddInstanceFieldToExistingType; } + // Inserting a member into an existing generic type, or a generic method into a type is only allowed if the runtime supports it + if (newSymbol is not INamedTypeSymbol && InGenericContext(newSymbol)) + { + requiredCapabilities |= newSymbol is IFieldSymbol ? EditAndContinueCapabilities.GenericAddFieldToExistingType : EditAndContinueCapabilities.GenericAddMethodToExistingType; + } + return capabilities.Grant(requiredCapabilities); } + private static bool CanUpdateMemberBody(ISymbol oldSymbol, ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities) + { + if (InGenericContext(oldSymbol) || InGenericContext(newSymbol)) + { + return capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod); + } + + return true; + } + private static void AddEditsForSynthesizedRecordMembers(Compilation compilation, INamedTypeSymbol recordType, ArrayBuilder semanticEdits, CancellationToken cancellationToken) { foreach (var member in GetRecordUpdatedSynthesizedMembers(compilation, recordType)) @@ -4623,7 +4693,7 @@ RudeEditKind.GenericMethodUpdate or private void ReportUpdateRudeEdit(ArrayBuilder diagnostics, RudeEditKind rudeEdit, ISymbol oldSymbol, ISymbol newSymbol, SyntaxNode? newNode, Compilation newCompilation, CancellationToken cancellationToken) { - if (newSymbol.IsImplicitlyDeclared) + if (newSymbol.IsImplicitlyDeclared && rudeEdit != RudeEditKind.GenericTypeUpdate) { ReportDeletedMemberRudeEdit(diagnostics, oldSymbol, newCompilation, rudeEdit, cancellationToken); } @@ -4908,7 +4978,6 @@ private static bool HasBackingField(IEventSymbol @event) newDeclaration = GetSymbolDeclarationSyntax(newCtor.DeclaringSyntaxReferences.Single(), cancellationToken); // Implicit record constructors are represented by the record declaration itself. - // https://github.com/dotnet/roslyn/issues/54403 var isPrimaryRecordConstructor = IsRecordDeclaration(newDeclaration); // Constructor that doesn't contain initializers had a corresponding semantic edit produced previously @@ -4962,15 +5031,18 @@ private static bool HasBackingField(IEventSymbol @event) // Report an error if the updated constructor's declaration is in the current document // and its body edit is disallowed (e.g. contains stackalloc). - if (oldCtor != null && newDeclaration.SyntaxTree == newSyntaxTree && anyInitializerUpdatesInCurrentDocument) + if (oldCtor != null && newDeclaration.SyntaxTree == newSyntaxTree && anyInitializerUpdatesInCurrentDocument && !isPrimaryRecordConstructor) { // attribute rude edit to one of the modified members var firstSpan = updatesInCurrentDocument.ChangedDeclarations.Keys.Where(IsDeclarationWithInitializer).Aggregate( (min: int.MaxValue, span: default(TextSpan)), (accumulate, node) => (node.SpanStart < accumulate.min) ? (node.SpanStart, node.Span) : accumulate).span; + var newBody = TryGetDeclarationBody(newDeclaration); + + Contract.ThrowIfNull(newBody); Contract.ThrowIfTrue(firstSpan.IsEmpty); - ReportMemberBodyUpdateRudeEdits(diagnostics, newDeclaration, firstSpan); + ReportMemberOrLambdaBodyUpdateRudeEditsImpl(diagnostics, newDeclaration, newBody, firstSpan); } // When explicitly implementing the copy constructor of a record the parameter name if the runtime doesn't support @@ -5114,10 +5186,12 @@ static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) private void ReportLambdaAndClosureRudeEdits( SemanticModel oldModel, + ISymbol oldMember, SyntaxNode oldMemberBody, SemanticModel newModel, SyntaxNode newMemberBody, ISymbol newMember, + Match memberBodyMatch, IReadOnlyDictionary? matchedLambdas, BidirectionalMap map, EditAndContinueCapabilitiesGrantor capabilities, @@ -5129,6 +5203,53 @@ static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) if (matchedLambdas != null) { + foreach (var (oldLambdaBody, newLambdaInfo) in matchedLambdas) + { + var newLambdaBody = newLambdaInfo.NewBody; + if (newLambdaBody == null) + { + continue; + } + + var lambdaBodyMatch = newLambdaInfo.Match; + Debug.Assert(lambdaBodyMatch != null); + + var oldStateMachineInfo = GetStateMachineInfo(oldLambdaBody); + var newStateMachineInfo = GetStateMachineInfo(newLambdaBody); + ReportStateMachineBodyUpdateRudeEdits(lambdaBodyMatch, oldStateMachineInfo, newLambdaBody, newStateMachineInfo, newLambdaInfo.HasActiveStatement, diagnostics); + + // When the delta IL of the containing method is emitted lambdas declared in it are also emitted. + // If the runtime does not support changing IL of the method (e.g. method containing stackalloc) + // we need to report a rude edit. + // If only trivia change the IL is going to be unchanged and only sequence points in the PDB change, + // so we do not report rude edits. + + var oldLambda = GetLambda(oldLambdaBody); + var newLambda = GetLambda(newLambdaBody); + + if (!AreEquivalentLambdaBodies(oldLambda, oldLambdaBody, newLambda, newLambdaBody)) + { + ReportMemberOrLambdaBodyUpdateRudeEdits( + diagnostics, + oldModel, + oldLambdaBody, + oldMember, + newLambda, + newLambdaBody, + newMember, + memberBodyMatch, + capabilities, + oldStateMachineInfo, + newStateMachineInfo); + + if ((IsGenericLocalFunction(oldLambda) || IsGenericLocalFunction(newLambda)) && + !capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, GetDiagnosticSpan(newLambda, EditKind.Update), newLambda, new[] { GetDisplayName(newLambda) })); + } + } + } + var anySignatureErrors = false; foreach (var (oldLambdaBody, newLambdaInfo) in matchedLambdas) { @@ -5180,11 +5301,11 @@ select clausesByQuery.First()) using var oldLambdaBodyEnumerator = GetLambdaBodies(oldMemberBody).GetEnumerator(); using var newLambdaBodyEnumerator = GetLambdaBodies(newMemberBody).GetEnumerator(); - var oldHasLambdas = oldLambdaBodyEnumerator.MoveNext(); - var newHasLambdas = newLambdaBodyEnumerator.MoveNext(); + var oldHasLambdasOrLocalFunctions = oldLambdaBodyEnumerator.MoveNext(); + var newHasLambdasOrLocalFunctions = newLambdaBodyEnumerator.MoveNext(); // Exit early if there are no lambdas in the method to avoid expensive data flow analysis: - if (!oldHasLambdas && !newHasLambdas) + if (!oldHasLambdasOrLocalFunctions && !newHasLambdasOrLocalFunctions) { return; } @@ -5293,6 +5414,29 @@ select clausesByQuery.First()) } } + // Removal: We don't allow removal of lambda that has captures from multiple scopes. + + var oldHasLambdas = false; + var oldHasMoreLambdas = oldHasLambdasOrLocalFunctions; + while (oldHasMoreLambdas) + { + var (oldLambda, oldLambdaBody1, oldLambdaBody2) = oldLambdaBodyEnumerator.Current; + + oldHasLambdas |= !IsLocalFunction(oldLambda); + + if (!map.Forward.ContainsKey(oldLambda)) + { + ReportMultiScopeCaptures(oldLambdaBody1, oldModel, oldCaptures, newCaptures, oldCapturesToClosureScopes, oldCapturesIndex, reverseCapturesMap, diagnostics, isInsert: false, cancellationToken: cancellationToken); + + if (oldLambdaBody2 != null) + { + ReportMultiScopeCaptures(oldLambdaBody2, oldModel, oldCaptures, newCaptures, oldCapturesToClosureScopes, oldCapturesIndex, reverseCapturesMap, diagnostics, isInsert: false, cancellationToken: cancellationToken); + } + } + + oldHasMoreLambdas = oldLambdaBodyEnumerator.MoveNext(); + } + // Report rude edits for lambdas added to the method. // We already checked that no new captures are introduced or removed. // We also need to make sure that no new parent frame links are introduced. @@ -5318,15 +5462,16 @@ select clausesByQuery.First()) var containingTypeDeclaration = TryGetContainingTypeDeclaration(newMemberBody); var isInInterfaceDeclaration = containingTypeDeclaration != null && IsInterfaceDeclaration(containingTypeDeclaration); + var isNewMemberInGenericContext = InGenericContext(newMember); - var newHasLambdaBodies = newHasLambdas; + var newHasLambdaBodies = newHasLambdasOrLocalFunctions; while (newHasLambdaBodies) { var (newLambda, newLambdaBody1, newLambdaBody2) = newLambdaBodyEnumerator.Current; if (!map.Reverse.ContainsKey(newLambda)) { - if (!CanAddNewLambda(newLambda, capabilities, matchedLambdas)) + if (!CanAddNewLambda(newLambda, newLambdaBody1, newLambdaBody2)) { diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertNotSupportedByRuntime, GetDiagnosticSpan(newLambda, EditKind.Insert), newLambda, new string[] { GetDisplayName(newLambda, EditKind.Insert) })); } @@ -5350,27 +5495,66 @@ select clausesByQuery.First()) newHasLambdaBodies = newLambdaBodyEnumerator.MoveNext(); } - // Similarly for addition. We don't allow removal of lambda that has captures from multiple scopes. + syntaxMapRequired = newHasLambdasOrLocalFunctions; - var oldHasMoreLambdas = oldHasLambdas; - while (oldHasMoreLambdas) + bool CanAddNewLambda(SyntaxNode newLambda, SyntaxNode newLambdaBody1, SyntaxNode? newLambdaBody2) { - var (oldLambda, oldLambdaBody1, oldLambdaBody2) = oldLambdaBodyEnumerator.Current; + // Adding a lambda/local function might result in + // 1) emitting a new closure type + // 2) adding method and a static field to an existing closure type + // + // We currently do not precisely determine whether or not a suitable closure type already exists + // as static closure types might be shared with lambdas defined in a different member of the containing type. + // See: https://github.com/dotnet/roslyn/issues/52759 + // + // Furthermore, if a new lambda captures a variable that is alredy captured by a local function then + // the closure type is converted from struct local function closure to a lambda display class. + // Similarly, when a new conversion from local function group to a delegate is added the closure type also changes. + // Both should be reported as rude edits during capture analysis. + // See https://github.com/dotnet/roslyn/issues/67323 - if (!map.Forward.ContainsKey(oldLambda)) + var isLocalFunction = IsLocalFunction(newLambda); + + // We assume that [2] is always required since the closure type might already exist. + var requiredCapabilities = EditAndContinueCapabilities.AddMethodToExistingType; + + var inGenericLocalContext = InGenericLocalContext(newLambda, newMemberBody); + + if (isNewMemberInGenericContext || inGenericLocalContext) { - ReportMultiScopeCaptures(oldLambdaBody1, oldModel, oldCaptures, newCaptures, oldCapturesToClosureScopes, oldCapturesIndex, reverseCapturesMap, diagnostics, isInsert: false, cancellationToken: cancellationToken); + requiredCapabilities |= EditAndContinueCapabilities.GenericAddMethodToExistingType; + } - if (oldLambdaBody2 != null) + // Static lambdas are cached in static fields, unless in generic local functions. + // If either body is static we need to require the capabilities. + var isLambdaCachedInField = + !inGenericLocalContext && + !isLocalFunction && + (GetAccessedCaptures(newLambdaBody1, newModel, newCaptures, newCapturesIndex).Equals(BitVector.Empty) || + newLambdaBody2 != null && GetAccessedCaptures(newLambdaBody2, newModel, newCaptures, newCapturesIndex).Equals(BitVector.Empty)); + + if (isLambdaCachedInField) + { + requiredCapabilities |= EditAndContinueCapabilities.AddStaticFieldToExistingType; + + // If we are in a generic type or a member then the closure type is generic and we are adding a static field to a generic type. + if (isNewMemberInGenericContext) { - ReportMultiScopeCaptures(oldLambdaBody2, oldModel, oldCaptures, newCaptures, oldCapturesToClosureScopes, oldCapturesIndex, reverseCapturesMap, diagnostics, isInsert: false, cancellationToken: cancellationToken); + requiredCapabilities |= EditAndContinueCapabilities.GenericAddFieldToExistingType; } } - oldHasMoreLambdas = oldLambdaBodyEnumerator.MoveNext(); - } + // If the old verison of the method had any lambdas the nwe know a closure type exists and a new one isn't needed. + // We also know that adding a local function won't create a new closure type. + // Otherwise, we assume a new type is needed. - syntaxMapRequired = newHasLambdas; + if (!oldHasLambdas && !isLocalFunction) + { + requiredCapabilities |= EditAndContinueCapabilities.NewTypeDefinition; + } + + return capabilities.Grant(requiredCapabilities); + } } private IEnumerable<(SyntaxNode lambda, SyntaxNode lambdaBody1, SyntaxNode? lambdaBody2)> GetLambdaBodies(SyntaxNode body) @@ -5384,31 +5568,6 @@ private IEnumerable<(SyntaxNode lambda, SyntaxNode lambdaBody1, SyntaxNode? lamb } } - private bool CanAddNewLambda(SyntaxNode newLambda, EditAndContinueCapabilitiesGrantor capabilities, IReadOnlyDictionary? matchedLambdas) - { - // New local functions mean new methods in existing classes - if (IsLocalFunction(newLambda)) - { - return capabilities.Grant(EditAndContinueCapabilities.AddMethodToExistingType); - } - - // New lambdas sometimes mean creating new helper classes, and sometimes mean new methods in exising helper classes - // Unfortunately we are limited here in what we can do here. See: https://github.com/dotnet/roslyn/issues/52759 - - // If there is already a lambda in the method then the new lambda would result in a new method in the existing helper class. - // This check is redundant with the below, once the limitation in the referenced issue is resolved - if (matchedLambdas is { Count: > 0 }) - { - return capabilities.Grant(EditAndContinueCapabilities.AddMethodToExistingType); - } - - // If there is already a lambda in the class then the new lambda would result in a new method in the existing helper class. - // If there isn't already a lambda in the class then the new lambda would result in a new helper class. - // Unfortunately right now we can't determine which of these is true so we have to just check both capabilities instead. - return capabilities.Grant(EditAndContinueCapabilities.NewTypeDefinition) && - capabilities.Grant(EditAndContinueCapabilities.AddMethodToExistingType); - } - private void ReportMultiScopeCaptures( SyntaxNode lambdaBody, SemanticModel model, @@ -5952,22 +6111,19 @@ private static bool AreEquivalentClosureScopes(SyntaxNode oldScopeOpt, SyntaxNod #region State Machines - private void ReportStateMachineRudeEdits( + private void ReportMissingStateMachineAttribute( Compilation oldCompilation, - ISymbol oldMember, + StateMachineInfo kinds, SyntaxNode newBody, ArrayBuilder diagnostics) { - // Only methods, local functions and anonymous functions can be async/iterators machines, - // but don't assume so to be resiliant against errors in code. - if (oldMember is not IMethodSymbol oldMethod) + var stateMachineAttributeQualifiedName = kinds switch { - return; - } - - var stateMachineAttributeQualifiedName = oldMethod.IsAsync - ? "System.Runtime.CompilerServices.AsyncStateMachineAttribute" - : "System.Runtime.CompilerServices.IteratorStateMachineAttribute"; + { IsIterator: true } and { IsAsync: true } => "System.Runtime.CompilerServices.AsyncIteratorStateMachineAttribute", + { IsIterator: true } => "System.Runtime.CompilerServices.IteratorStateMachineAttribute", + { IsAsync: true } => "System.Runtime.CompilerServices.AsyncStateMachineAttribute", + _ => throw ExceptionUtilities.UnexpectedValue(kinds) + }; // We assume that the attributes, if exist, are well formed. // If not an error will be reported during EnC delta emit. @@ -6030,6 +6186,7 @@ public bool Equals(TypedConstant x, TypedConstant y) x.Kind switch { TypedConstantKind.Array => x.Values.SequenceEqual(y.Values, TypedConstantComparer.Instance), + TypedConstantKind.Type => TypesEquivalent(x.Value as ITypeSymbol, y.Value as ITypeSymbol, exact: false), _ => object.Equals(x.Value, y.Value) }; @@ -6052,7 +6209,7 @@ public int GetHashCode(KeyValuePair obj) private static bool IsGlobalMain(ISymbol symbol) => symbol is IMethodSymbol { Name: WellKnownMemberNames.TopLevelStatementsEntryPointMethodName }; - private static bool InGenericContext(ISymbol symbol, out bool isGenericMethod) + private static bool InGenericContext(ISymbol symbol) { var current = symbol; @@ -6060,25 +6217,40 @@ private static bool InGenericContext(ISymbol symbol, out bool isGenericMethod) { if (current is IMethodSymbol { Arity: > 0 }) { - isGenericMethod = true; return true; } if (current is INamedTypeSymbol { Arity: > 0 }) { - isGenericMethod = false; return true; } current = current.ContainingSymbol; if (current == null) { - isGenericMethod = false; return false; } } } + private bool InGenericLocalContext(SyntaxNode node, SyntaxNode containingMemberBody) + { + var current = node; + + while (current != containingMemberBody) + { + if (IsGenericLocalFunction(current)) + { + return true; + } + + current = current.Parent; + Contract.ThrowIfNull(current); + } + + return false; + } + #endregion #region Testing @@ -6098,22 +6270,10 @@ internal void ReportTopLevelSyntacticRudeEdits(ArrayBuilder internal BidirectionalMap ComputeMap( Match bodyMatch, - ArrayBuilder activeNodes, - ref Dictionary? lazyActiveOrMatchedLambdas, - ArrayBuilder diagnostics) - { - return _abstractEditAndContinueAnalyzer.ComputeMap(bodyMatch, activeNodes, ref lazyActiveOrMatchedLambdas, diagnostics); - } - - internal Match ComputeBodyMatch( - SyntaxNode oldBody, - SyntaxNode newBody, - ActiveNode[] activeNodes, - ArrayBuilder diagnostics, - out bool oldHasStateMachineSuspensionPoint, - out bool newHasStateMachineSuspensionPoint) + ArrayBuilder memberBodyActiveNodes, + ref Dictionary? lazyActiveOrMatchedLambdas) { - return _abstractEditAndContinueAnalyzer.ComputeBodyMatch(oldBody, newBody, activeNodes, diagnostics, out oldHasStateMachineSuspensionPoint, out newHasStateMachineSuspensionPoint); + return _abstractEditAndContinueAnalyzer.ComputeMap(bodyMatch, memberBodyActiveNodes, ref lazyActiveOrMatchedLambdas); } } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs index 68ec1f0334f94..5f8cb19b5d43a 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs @@ -50,6 +50,21 @@ internal enum EditAndContinueCapabilities /// Whether the runtime supports updating the Param table, and hence related edits (eg parameter renames) /// UpdateParameters = 1 << 6, + + /// + /// Adding a static or instance method, property or event to an existing type (without backing fields), such that the method and/or the type are generic. + /// + GenericAddMethodToExistingType = 1 << 7, + + /// + /// Updating an existing static or instance method, property or event (without backing fields) that is generic and/or contained in a generic type. + /// + GenericUpdateMethod = 1 << 8, + + /// + /// Adding a static or instance field to an existing generic type. + /// + GenericAddFieldToExistingType = 1 << 9, } internal static class EditAndContinueCapabilitiesParser @@ -69,6 +84,9 @@ public static EditAndContinueCapabilities Parse(ImmutableArray capabilit nameof(EditAndContinueCapabilities.NewTypeDefinition) => EditAndContinueCapabilities.NewTypeDefinition, nameof(EditAndContinueCapabilities.ChangeCustomAttributes) => EditAndContinueCapabilities.ChangeCustomAttributes, nameof(EditAndContinueCapabilities.UpdateParameters) => EditAndContinueCapabilities.UpdateParameters, + nameof(EditAndContinueCapabilities.GenericAddMethodToExistingType) => EditAndContinueCapabilities.GenericAddMethodToExistingType, + nameof(EditAndContinueCapabilities.GenericUpdateMethod) => EditAndContinueCapabilities.GenericUpdateMethod, + nameof(EditAndContinueCapabilities.GenericAddFieldToExistingType) => EditAndContinueCapabilities.GenericAddFieldToExistingType, // To make it eaiser for runtimes to specify more broad capabilities "AddDefinitionToExistingType" => EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType, diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index 581f1a07d5bd0..9441f658f5ed8 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -93,14 +93,10 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.InsertOperator, nameof(FeaturesResources.Adding_a_user_defined_0_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.InsertIntoStruct, nameof(FeaturesResources.Adding_0_into_a_1_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.InsertIntoClassWithLayout, nameof(FeaturesResources.Adding_0_into_a_class_with_explicit_or_sequential_layout_requires_restarting_the_application)); - AddRudeEdit(RudeEditKind.InsertGenericMethod, nameof(FeaturesResources.Adding_a_generic_0_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.Move, nameof(FeaturesResources.Moving_0_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.Delete, nameof(FeaturesResources.Deleting_0_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.GenericMethodUpdate, nameof(FeaturesResources.Modifying_a_generic_method_requires_restarting_the_application)); - AddRudeEdit(RudeEditKind.GenericMethodTriviaUpdate, nameof(FeaturesResources.Modifying_whitespace_or_comments_in_a_generic_0_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.GenericTypeUpdate, nameof(FeaturesResources.Modifying_a_method_inside_the_context_of_a_generic_type_requires_restarting_the_application)); - AddRudeEdit(RudeEditKind.GenericTypeTriviaUpdate, nameof(FeaturesResources.Modifying_whitespace_or_comments_in_0_inside_the_context_of_a_generic_type_requires_restarting_the_application)); - AddRudeEdit(RudeEditKind.GenericTypeInitializerUpdate, nameof(FeaturesResources.Modifying_the_initializer_of_0_in_a_generic_type_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, nameof(FeaturesResources.Adding_a_constructor_to_a_type_with_a_field_or_property_initializer_that_contains_an_anonymous_function_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.RenamingCapturedVariable, nameof(FeaturesResources.Renaming_a_captured_variable_from_0_to_1_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.StackAllocUpdate, nameof(FeaturesResources.Modifying_0_which_contains_the_stackalloc_operator_requires_restarting_the_application)); @@ -137,7 +133,6 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.MemberBodyInternalError, nameof(FeaturesResources.Modifying_body_of_0_requires_restarting_the_application_due_to_internal_error_1)); AddRudeEdit(RudeEditKind.MemberBodyTooBig, nameof(FeaturesResources.Modifying_body_of_0_requires_restarting_the_application_because_the_body_has_too_many_statements)); AddRudeEdit(RudeEditKind.SourceFileTooBig, nameof(FeaturesResources.Modifying_source_file_0_requires_restarting_the_application_because_the_file_is_too_big)); - AddRudeEdit(RudeEditKind.InsertIntoGenericType, nameof(FeaturesResources.Adding_0_into_a_generic_type_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.ImplementRecordParameterAsReadOnly, nameof(FeaturesResources.Implementing_a_record_positional_parameter_0_as_read_only_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.ImplementRecordParameterWithSet, nameof(FeaturesResources.Implementing_a_record_positional_parameter_0_with_a_set_accessor_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, nameof(FeaturesResources.Explicitly_implemented_methods_of_records_must_have_parameter_names_that_match_the_compiler_generated_equivalent_0)); @@ -156,6 +151,8 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.ChangingNamespace, nameof(FeaturesResources.Changing_the_containing_namespace_of_0_from_1_to_2_requires_restarting_th_application)); AddRudeEdit(RudeEditKind.ChangingTypeNotSupportedByRuntime, nameof(FeaturesResources.Changing_the_type_of_0_requires_restarting_the_application)); AddRudeEdit(RudeEditKind.DeleteNotSupportedByRuntime, nameof(FeaturesResources.Deleting_0_requires_restarting_the_application_because_is_not_supported_by_the_runtime)); + AddRudeEdit(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, nameof(FeaturesResources.Updating_async_or_iterator_requires_restarting_the_application_because_is_not_supported_by_the_runtime)); + AddRudeEdit(RudeEditKind.UpdatingGenericNotSupportedByRuntime, nameof(FeaturesResources.Updating_0_within_generic_type_or_method_requires_restarting_the_application_because_is_not_supported_by_the_runtime)); // VB specific AddRudeEdit(RudeEditKind.HandlesClauseUpdate, nameof(FeaturesResources.Updating_the_Handles_clause_of_0_requires_restarting_the_application)); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs index f99e64a5b3760..0eb7971e96537 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticUpdateSource.cs @@ -104,7 +104,7 @@ public void ReportDiagnostics(Workspace workspace, Solution solution, ImmutableA if (documentDiagnostics.Length > 0) { - foreach (var (documentId, diagnosticData) in documentDiagnostics.ToDictionary(data => data.DocumentId!)) + foreach (var (documentId, diagnosticData) in documentDiagnostics.ToDictionary(static data => data.DocumentId!)) { var diagnosticGroupId = (this, documentId); @@ -120,7 +120,7 @@ public void ReportDiagnostics(Workspace workspace, Solution solution, ImmutableA if (projectDiagnostics.Length > 0) { - foreach (var (projectId, diagnosticData) in projectDiagnostics.ToDictionary(data => data.ProjectId!)) + foreach (var (projectId, diagnosticData) in projectDiagnostics.ToDictionary(static data => data.ProjectId!)) { var diagnosticGroupId = (this, projectId); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs index 811bb90b579c8..f37506085e232 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDocumentAnalysesCache.cs @@ -188,7 +188,7 @@ private AsyncLazy GetDocumentAnalysisNoLock(Project bas return analysis.results; } - var lazyResults = new AsyncLazy( + var lazyResults = AsyncLazy.Create( asynchronousComputeFunction: async cancellationToken => { try @@ -200,8 +200,7 @@ private AsyncLazy GetDocumentAnalysisNoLock(Project bas { throw ExceptionUtilities.Unreachable(); } - }, - cacheResult: true); + }); // Previous results for this document id are discarded as they are no longer relevant. // The only relevant analysis is for the latest base and document snapshots. diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index 9b2f3bddc571c..8f668b75b346a 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -110,10 +110,10 @@ internal sealed class EditSession telemetry.SetBreakState(inBreakState); BaseActiveStatements = lazyActiveStatementMap ?? (inBreakState - ? new AsyncLazy(GetBaseActiveStatementsAsync, cacheResult: true) + ? AsyncLazy.Create(GetBaseActiveStatementsAsync) : new AsyncLazy(ActiveStatementsMap.Empty)); - Capabilities = new AsyncLazy(GetCapabilitiesAsync, cacheResult: true); + Capabilities = AsyncLazy.Create(GetCapabilitiesAsync); Analyses = new EditAndContinueDocumentAnalysesCache(BaseActiveStatements, Capabilities); } diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs index 126d70f1c518c..aa9b181c18b44 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs @@ -57,7 +57,7 @@ public async ValueTask BreakStateOrCapabilitiesChangedAsync(IDiagnosticAnalyzerS } // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, documentIds: documentsToReanalyze); + diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: documentsToReanalyze, highPriority: false); // clear emit/apply diagnostics reported previously: diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: false); @@ -85,7 +85,7 @@ public async ValueTask EndDebuggingSessionAsync(Solution compileTimeSolution, Ed compileTimeSolution, documentsToReanalyze, designTimeSolution: _workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, documentIds: designTimeDocumentsToReanalyze); + diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: designTimeDocumentsToReanalyze, highPriority: false); // clear emit/apply diagnostics reported previously: diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: true); @@ -163,7 +163,7 @@ public async ValueTask EndDebuggingSessionAsync(Solution compileTimeSolution, Ed diagnosticUpdateSource.ClearDiagnostics(isSessionEnding: false); // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, documentIds: rudeEdits.Select(d => d.DocumentId)); + diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: rudeEdits.Select(d => d.DocumentId), highPriority: false); // report emit/apply diagnostics: diagnosticUpdateSource.ReportDiagnostics(_workspace, solution, diagnosticData, rudeEdits); @@ -190,7 +190,7 @@ public async ValueTask CommitSolutionUpdateAsync(IDiagnosticAnalyzerService diag } // clear all reported rude edits: - diagnosticService.Reanalyze(_workspace, documentIds: documentsToReanalyze); + diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: documentsToReanalyze, highPriority: false); } public async ValueTask DiscardSolutionUpdateAsync(CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 1a80974d810b6..56b6fad495d43 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -41,7 +41,7 @@ internal enum RudeEditKind : ushort InsertExtern = 25, InsertOperator = 26, // InsertNonPublicConstructor = 27, - InsertGenericMethod = 28, + // InsertGenericMethod = 28, InsertDllImport = 29, InsertIntoStruct = 30, InsertIntoClassWithLayout = 31, @@ -50,10 +50,10 @@ internal enum RudeEditKind : ushort // MethodBodyAdd = 34, // MethodBodyDelete = 35, GenericMethodUpdate = 36, - GenericMethodTriviaUpdate = 37, + // GenericMethodTriviaUpdate = 37, GenericTypeUpdate = 38, - GenericTypeTriviaUpdate = 39, - GenericTypeInitializerUpdate = 40, + // GenericTypeTriviaUpdate = 39, + // GenericTypeInitializerUpdate = 40, // PartialTypeInitializerUpdate = 41, // AsyncMethodUpdate = 42, // AsyncMethodTriviaUpdate = 43, @@ -116,7 +116,7 @@ internal enum RudeEditKind : ushort MemberBodyInternalError = 88, SourceFileTooBig = 89, MemberBodyTooBig = 90, - InsertIntoGenericType = 91, + // InsertIntoGenericType = 91, ImplementRecordParameterAsReadOnly = 92, ImplementRecordParameterWithSet = 93, @@ -138,5 +138,7 @@ internal enum RudeEditKind : ushort ChangingNamespace = 109, ChangingTypeNotSupportedByRuntime = 110, DeleteNotSupportedByRuntime = 111, + UpdatingStateMachineMethodNotSupportedByRuntime = 112, + UpdatingGenericNotSupportedByRuntime = 113, } } diff --git a/src/Features/Core/Portable/EditAndContinue/StateMachineInfo.cs b/src/Features/Core/Portable/EditAndContinue/StateMachineInfo.cs new file mode 100644 index 0000000000000..1f83c1293405f --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/StateMachineInfo.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. + +namespace Microsoft.CodeAnalysis.EditAndContinue; + +/// +/// Describes a method, lambda or local function that generates a state machine. +/// +/// +/// If the method is marked as async and generates a state machine. +/// Method marked with async keyword in C# and VB generates a state machine even if it doesn't have any await expressions ( is false). +/// +/// +/// If the method is marked as iterator and generates a state machine. +/// In C# an (async) iterator method must have a yield statement ( is true, is true). +/// In VB a method without a Yield statement can be marked as an Iterator ( is true, may be false). +/// +/// +/// True if any awaits and/or yields are present in the method. +/// +internal readonly record struct StateMachineInfo(bool IsAsync, bool IsIterator, bool HasSuspensionPoints) +{ + public static StateMachineInfo None = default; + + /// + /// True if a state machine is generated. + /// + public bool IsStateMachine => IsAsync || IsIterator; +} diff --git a/src/Features/Core/Portable/EditAndContinue/StateMachineKinds.cs b/src/Features/Core/Portable/EditAndContinue/StateMachineKinds.cs deleted file mode 100644 index 1dead802eb97b..0000000000000 --- a/src/Features/Core/Portable/EditAndContinue/StateMachineKinds.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace Microsoft.CodeAnalysis.EditAndContinue -{ - [Flags] - internal enum StateMachineKinds - { - None = 0, - Async = 1, - Iterator = 1 << 1, - } -} diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs b/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs index ade484a48b6ff..b150afbd92400 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Classification/AbstractEmbeddedLanguageClassificationService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.EmbeddedLanguages; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; @@ -35,14 +36,14 @@ internal abstract class AbstractEmbeddedLanguageClassificationService : } public async Task AddEmbeddedLanguageClassificationsAsync( - Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); AddEmbeddedLanguageClassifications(document.Project, semanticModel, textSpan, options, result, cancellationToken); } public void AddEmbeddedLanguageClassifications( - Project? project, SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + Project? project, SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { if (project is null) return; @@ -59,7 +60,7 @@ internal abstract class AbstractEmbeddedLanguageClassificationService : private readonly SemanticModel _semanticModel; private readonly TextSpan _textSpan; private readonly ClassificationOptions _options; - private readonly ArrayBuilder _result; + private readonly SegmentedList _result; private readonly CancellationToken _cancellationToken; public Worker( @@ -68,7 +69,7 @@ internal abstract class AbstractEmbeddedLanguageClassificationService : SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { _owner = service; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Classification/EmbeddedLanguageClassifierContext.cs b/src/Features/Core/Portable/EmbeddedLanguages/Classification/EmbeddedLanguageClassifierContext.cs index 8923e80924cca..c7f71ee6277f5 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Classification/EmbeddedLanguageClassifierContext.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Classification/EmbeddedLanguageClassifierContext.cs @@ -3,15 +3,15 @@ // See the LICENSE file in the project root for more information. using System.Threading; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Classification { internal readonly struct EmbeddedLanguageClassificationContext { - private readonly ArrayBuilder _result; + private readonly SegmentedList _result; public Project Project { get; } @@ -36,7 +36,7 @@ namespace Microsoft.CodeAnalysis.Classification SyntaxToken syntaxToken, ClassificationOptions options, IVirtualCharService virtualCharService, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { Project = project; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs index 364944c6d154a..9b895e6db21c8 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs @@ -125,7 +125,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) properties: properties.ToImmutable(), rules: embeddedItem.IsDefault ? s_rules.WithMatchPriority(MatchPriority.Preselect) - : s_rules)); + : s_rules, + isComplexTextEdit: context.CompletionListSpan != textChange.Span)); } context.IsExclusive = true; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedCompletionProvider.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedCompletionProvider.cs index 224313bd6b7a1..5107234a1498c 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedCompletionProvider.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegexEmbeddedCompletionProvider.cs @@ -119,7 +119,8 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) inlineDescription: embeddedItem.InlineDescription, sortText: sortText, properties: properties.ToImmutable(), - rules: s_rules)); + rules: s_rules, + isComplexTextEdit: context.CompletionListSpan != textChange.Span)); } context.IsExclusive = true; diff --git a/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs b/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs index eb7ff48417298..38e4a4c6c43fa 100644 --- a/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs +++ b/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs @@ -21,7 +21,7 @@ public EncapsulateFieldResult(string name, Glyph glyph, Func(getSolutionAsync, cacheResult: true); + _lazySolution = AsyncLazy.Create(getSolutionAsync); } public Task GetSolutionAsync(CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs index 519ce960d837e..3782d4ad4888c 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs @@ -547,7 +547,7 @@ private static void AddVariableToMap(IDictionary variable return true; } - if (UserDefinedValueType(model.Compilation, type) && !SelectionResult.Options.DontPutOutOrRefOnStruct) + if (UserDefinedValueType(model.Compilation, type) && !SelectionResult.Options.DoNotPutOutOrRefOnStruct) { variableStyle = AlwaysReturn(variableStyle); return true; diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 33fd3f520d84f..1137eac7109a1 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -414,9 +414,6 @@ Adding {0} into an interface requires restarting the application. - - Adding {0} into a generic type requires restarting the application. - Adding {0} into an interface method requires restarting the application. @@ -496,9 +493,6 @@ Adding a user defined {0} requires restarting the application. - - Adding a generic {0} requires restarting the application. - Adding {0} around an active statement requires restarting the application. @@ -524,18 +518,9 @@ Modifying a generic method requires restarting the application. - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Modifying a method inside the context of a generic type requires restarting the application. - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - - - Modifying the initializer of {0} in a generic type requires restarting the application. - Adding a constructor to a type with a field or property initializer that contains an anonymous function requires restarting the application. @@ -3121,6 +3106,12 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Deleting {0} requires restarting the application because is not supported by the runtime. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Extract base record... {Locked="record"} "record" is a language construct for C# and should not be localized. diff --git a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs index 2909353719649..ab5fe44db91d8 100644 --- a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs @@ -25,11 +25,13 @@ internal abstract partial class AbstractIntroduceParameterCodeRefactoringProvide TExpressionSyntax, TInvocationExpressionSyntax, TObjectCreationExpressionSyntax, - TIdentifierNameSyntax> : CodeRefactoringProvider + TIdentifierNameSyntax, + TArgumentSyntax> : CodeRefactoringProvider where TExpressionSyntax : SyntaxNode where TInvocationExpressionSyntax : TExpressionSyntax where TObjectCreationExpressionSyntax : TExpressionSyntax where TIdentifierNameSyntax : TExpressionSyntax + where TArgumentSyntax : SyntaxNode { private enum IntroduceParameterCodeActionKind { @@ -39,7 +41,7 @@ private enum IntroduceParameterCodeActionKind } protected abstract SyntaxNode GenerateExpressionFromOptionalParameter(IParameterSymbol parameterSymbol); - protected abstract SyntaxNode UpdateArgumentListSyntax(SyntaxNode argumentList, SeparatedSyntaxList arguments); + protected abstract SyntaxNode UpdateArgumentListSyntax(SyntaxNode argumentList, SeparatedSyntaxList arguments); protected abstract SyntaxNode? GetLocalDeclarationFromDeclarator(SyntaxNode variableDecl); protected abstract bool IsDestructor(IMethodSymbol methodSymbol); diff --git a/src/Features/Core/Portable/IntroduceParameter/IntroduceParameterDocumentRewriter.cs b/src/Features/Core/Portable/IntroduceParameter/IntroduceParameterDocumentRewriter.cs index 65ecf798fb077..fc6ee428c1b40 100644 --- a/src/Features/Core/Portable/IntroduceParameter/IntroduceParameterDocumentRewriter.cs +++ b/src/Features/Core/Portable/IntroduceParameter/IntroduceParameterDocumentRewriter.cs @@ -18,11 +18,11 @@ namespace Microsoft.CodeAnalysis.IntroduceParameter { - internal abstract partial class AbstractIntroduceParameterCodeRefactoringProvider + internal abstract partial class AbstractIntroduceParameterCodeRefactoringProvider { private class IntroduceParameterDocumentRewriter { - private readonly AbstractIntroduceParameterCodeRefactoringProvider _service; + private readonly AbstractIntroduceParameterCodeRefactoringProvider _service; private readonly Document _originalDocument; private readonly SyntaxGenerator _generator; private readonly ISyntaxFactsService _syntaxFacts; @@ -35,7 +35,7 @@ private class IntroduceParameterDocumentRewriter private readonly bool _allOccurrences; public IntroduceParameterDocumentRewriter( - AbstractIntroduceParameterCodeRefactoringProvider service, + AbstractIntroduceParameterCodeRefactoringProvider service, Document originalDocument, TExpressionSyntax expression, IMethodSymbol methodSymbol, @@ -320,7 +320,7 @@ private int GetInsertionIndex(Compilation compilation) var invocationArguments = _syntaxFacts.GetArgumentsOfArgumentList(argumentListSyntax); parameterToArgumentMap.Clear(); MapParameterToArgumentsAtInvocation(parameterToArgumentMap, invocationArguments, invocationSemanticModel, cancellationToken); - var currentInvocationArguments = _syntaxFacts.GetArgumentsOfArgumentList(currentArgumentListSyntax); + var currentInvocationArguments = (SeparatedSyntaxList)_syntaxFacts.GetArgumentsOfArgumentList(currentArgumentListSyntax); var requiredArguments = new List(); foreach (var parameterSymbol in validParameters) @@ -335,7 +335,7 @@ private int GetInsertionIndex(Compilation compilation) var named = ShouldArgumentBeNamed(compilation, invocationSemanticModel, invocationArguments, insertionIndex, cancellationToken); var newMethodInvocation = GenerateNewMethodInvocation(invocation, requiredArguments, newMethodIdentifier); - SeparatedSyntaxList allArguments; + SeparatedSyntaxList allArguments; if (conditionalRoot is null) { allArguments = AddArgumentToArgumentList(currentInvocationArguments, newMethodInvocation, parameterName, insertionIndex, named); @@ -521,7 +521,7 @@ private async Task GenerateNewMethodOverloadAsync(int insertionIndex { editor.ReplaceNode(argumentListSyntax, (currentArgumentListSyntax, _) => { - var updatedInvocationArguments = _syntaxFacts.GetArgumentsOfArgumentList(currentArgumentListSyntax); + var updatedInvocationArguments = (SeparatedSyntaxList)_syntaxFacts.GetArgumentsOfArgumentList(currentArgumentListSyntax); var updatedExpression = CreateNewArgumentExpression(expressionEditor, expressionToParameterMap, parameterToArgumentMap, updatedInvocationArguments); var named = ShouldArgumentBeNamed(compilation, invocationSemanticModel, invocationArguments, insertionIndex, cancellationToken); var allArguments = AddArgumentToArgumentList(updatedInvocationArguments, @@ -579,13 +579,13 @@ private async Task GenerateNewMethodOverloadAsync(int insertionIndex /// If the parameter is optional and the invocation does not specify the parameter, then /// a named argument needs to be introduced. /// - private SeparatedSyntaxList AddArgumentToArgumentList( - SeparatedSyntaxList invocationArguments, SyntaxNode newArgumentExpression, + private SeparatedSyntaxList AddArgumentToArgumentList( + SeparatedSyntaxList invocationArguments, SyntaxNode newArgumentExpression, string parameterName, int insertionIndex, bool named) { var argument = named - ? _generator.Argument(parameterName, RefKind.None, newArgumentExpression) - : _generator.Argument(newArgumentExpression); + ? (TArgumentSyntax)_generator.Argument(parameterName, RefKind.None, newArgumentExpression) + : (TArgumentSyntax)_generator.Argument(newArgumentExpression); return invocationArguments.Insert(insertionIndex, argument); } diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 7dca668880ec3..3b57a428d8359 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 full true @@ -74,10 +74,13 @@ + + + diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs index a1522509ae84a..00a04fadbae6b 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.CachedDocumentSearch.cs @@ -158,9 +158,8 @@ private static void ClearCachedData() // match on disk anymore. var asyncLazy = cachedIndexMap.GetOrAdd( (storageService, documentKey, stringTable), - static t => new AsyncLazy( - c => TopLevelSyntaxTreeIndex.LoadAsync( - t.service, t.documentKey, checksum: null, t.stringTable, c), cacheResult: true)); + static t => AsyncLazy.Create( + c => TopLevelSyntaxTreeIndex.LoadAsync(t.service, t.documentKey, checksum: null, t.stringTable, c))); return asyncLazy.GetValueAsync(cancellationToken); } } diff --git a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs index 9def945813616..1b27e5df62b91 100644 --- a/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs +++ b/src/Features/Core/Portable/NavigateTo/AbstractNavigateToSearchService.InProcess.cs @@ -108,7 +108,7 @@ internal abstract partial class AbstractNavigateToSearchService CancellationToken cancellationToken) { var containerMatcher = patternContainer != null - ? PatternMatcher.CreateDotSeparatedContainerMatcher(patternContainer) + ? PatternMatcher.CreateDotSeparatedContainerMatcher(patternContainer, includeMatchedSpans: true) : null; using var nameMatcher = PatternMatcher.CreatePatternMatcher(patternName, includeMatchedSpans: true, allowFuzzyMatching: true); diff --git a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs b/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs index 861e040808079..5011f835253da 100644 --- a/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/ISolutionCrawlerService.cs @@ -15,8 +15,10 @@ internal interface ISolutionCrawlerService : IWorkspaceService /// /// Ask solution crawler to re-analyze given s or/and s /// in given with given . + /// If both and are null, the entire + /// for the given is re-analyzed. /// - void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false); + void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority); /// /// Get for the given diff --git a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs index 3e701621204b2..a4aa8162ef3be 100644 --- a/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/SolutionCrawlerService.cs @@ -30,7 +30,7 @@ public SolutionCrawlerService() { } - public void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false) + public void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) { // if solution crawler doesn't exist for the given workspace. don't do anything if (workspace.Services.GetService() is SolutionCrawlerRegistrationService registration) diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs index ebf365267657d..55424de5fd6c5 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncDocumentWorkItemQueue.cs @@ -46,7 +46,7 @@ protected override bool TryTake_NoLock(DocumentId key, out WorkItem workInfo) } protected override bool TryTakeAnyWork_NoLock( - ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, IDiagnosticAnalyzerService? service, + ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem) { // there must be at least one item in the map when this is called unless host is shutting down. @@ -56,7 +56,7 @@ protected override bool TryTake_NoLock(DocumentId key, out WorkItem workInfo) return false; } - var documentId = GetBestDocumentId_NoLock(preferableProjectId, dependencyGraph, service); + var documentId = GetBestDocumentId_NoLock(preferableProjectId, dependencyGraph); if (TryTake_NoLock(documentId, out workItem)) { return true; @@ -66,9 +66,9 @@ protected override bool TryTake_NoLock(DocumentId key, out WorkItem workInfo) } private DocumentId GetBestDocumentId_NoLock( - ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, IDiagnosticAnalyzerService? analyzerService) + ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph) { - var projectId = GetBestProjectId_NoLock(_documentWorkQueue, preferableProjectId, dependencyGraph, analyzerService); + var projectId = GetBestProjectId_NoLock(_documentWorkQueue, preferableProjectId, dependencyGraph); var documentMap = _documentWorkQueue[projectId]; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs index 932b3ec2fbc41..5620d684632bd 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncProjectWorkItemQueue.cs @@ -48,7 +48,7 @@ protected override bool TryTake_NoLock(ProjectId key, out WorkItem workInfo) } protected override bool TryTakeAnyWork_NoLock( - ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, IDiagnosticAnalyzerService? analyzerService, + ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem) { // there must be at least one item in the map when this is called unless host is shutting down. @@ -58,7 +58,7 @@ protected override bool TryTake_NoLock(ProjectId key, out WorkItem workInfo) return false; } - var projectId = GetBestProjectId_NoLock(_projectWorkQueue, preferableProjectId, dependencyGraph, analyzerService); + var projectId = GetBestProjectId_NoLock(_projectWorkQueue, preferableProjectId, dependencyGraph); if (TryTake_NoLock(projectId, out workItem)) { return true; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs index 1a2d37c892f95..a9bca58c504d7 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AsyncWorkItemQueue.cs @@ -46,7 +46,7 @@ public AsyncWorkItemQueue(SolutionCrawlerProgressReporter progressReporter, Work protected abstract bool TryTake_NoLock(TKey key, out WorkItem workInfo); - protected abstract bool TryTakeAnyWork_NoLock(ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, IDiagnosticAnalyzerService? service, out WorkItem workItem); + protected abstract bool TryTakeAnyWork_NoLock(ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem); public int WorkItemCount { @@ -217,14 +217,13 @@ public bool TryTake(TKey key, out WorkItem workInfo, out CancellationToken cance public bool TryTakeAnyWork( ProjectId? preferableProjectId, ProjectDependencyGraph dependencyGraph, - IDiagnosticAnalyzerService? analyzerService, out WorkItem workItem, out CancellationToken cancellationToken) { lock (_gate) { // there must be at least one item in the map when this is called unless host is shutting down. - if (TryTakeAnyWork_NoLock(preferableProjectId, dependencyGraph, analyzerService, out workItem)) + if (TryTakeAnyWork_NoLock(preferableProjectId, dependencyGraph, out workItem)) { cancellationToken = GetNewCancellationToken_NoLock(workItem.Key); workItem.AsyncToken.Dispose(); @@ -248,9 +247,9 @@ protected CancellationToken GetNewCancellationToken_NoLock(object key) return source.Token; } - protected ProjectId GetBestProjectId_NoLock( + protected static ProjectId GetBestProjectId_NoLock( Dictionary workQueue, ProjectId? projectId, - ProjectDependencyGraph dependencyGraph, IDiagnosticAnalyzerService? analyzerService) + ProjectDependencyGraph dependencyGraph) { if (projectId != null) { @@ -259,26 +258,17 @@ protected CancellationToken GetNewCancellationToken_NoLock(object key) return projectId; } - // prefer project that directly depends on the given project and has diagnostics as next project to + // prefer project that directly depends on the given project as next project to // process foreach (var dependingProjectId in dependencyGraph.GetProjectsThatDirectlyDependOnThisProject(projectId)) { - if (workQueue.ContainsKey(dependingProjectId) && analyzerService?.ContainsDiagnostics(Workspace, dependingProjectId) == true) + if (workQueue.ContainsKey(dependingProjectId)) { return dependingProjectId; } } } - // prefer a project that has diagnostics as next project to process. - foreach (var pendingProjectId in workQueue.Keys) - { - if (analyzerService?.ContainsDiagnostics(Workspace, pendingProjectId) == true) - { - return pendingProjectId; - } - } - // explicitly iterate so that we can use struct enumerator foreach (var pair in workQueue) { diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs index 58478a5564e12..90ace94ca558f 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs @@ -174,7 +174,6 @@ private bool GetNextWorkItem(out WorkItem workItem, out CancellationToken cancel return _workItemQueue.TryTakeAnyWork( preferableProjectId: null, dependencyGraph: _processor.DependencyGraph, - analyzerService: _processor.DiagnosticAnalyzerService, workItem: out workItem, cancellationToken: out cancellationToken); } diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs index d80b5ce182653..f7a12132c0346 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs @@ -38,9 +38,6 @@ private partial class IncrementalAnalyzerProcessor private readonly NormalPriorityProcessor _normalPriorityProcessor; private readonly LowPriorityProcessor _lowPriorityProcessor; - // NOTE: IDiagnosticAnalyzerService can be null in test environment. - private readonly Lazy _lazyDiagnosticAnalyzerService; - /// /// The keys in this are either a string or a (string, Guid) tuple. See /// for what is writing this out. @@ -60,8 +57,6 @@ private partial class IncrementalAnalyzerProcessor _listener = listener; _registration = registration; - _lazyDiagnosticAnalyzerService = new Lazy(() => GetDiagnosticAnalyzerService(analyzerProviders)); - var analyzersGetter = new AnalyzersGetter(analyzerProviders); // create analyzers lazily. @@ -86,13 +81,6 @@ private partial class IncrementalAnalyzerProcessor _lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpan, shutdownToken); } - private static IDiagnosticAnalyzerService? GetDiagnosticAnalyzerService(IEnumerable> analyzerProviders) - { - // alternatively, we could just MEF import IDiagnosticAnalyzerService directly - // this can be null in test env. - return (IDiagnosticAnalyzerService?)analyzerProviders.Where(p => p.Value is IDiagnosticAnalyzerService).SingleOrDefault()?.Value; - } - private static ImmutableArray GetIncrementalAnalyzers(Registration registration, AnalyzersGetter analyzersGetter, bool onlyHighPriorityAnalyzer) { var orderedAnalyzers = analyzersGetter.GetOrderedAnalyzers(registration.Workspace, onlyHighPriorityAnalyzer); @@ -133,7 +121,6 @@ public void Shutdown() public ImmutableArray Analyzers => _normalPriorityProcessor.Analyzers; private ProjectDependencyGraph DependencyGraph => _registration.GetSolutionToAnalyze().GetProjectDependencyGraph(); - private IDiagnosticAnalyzerService? DiagnosticAnalyzerService => _lazyDiagnosticAnalyzerService?.Value; public Task AsyncProcessorTask { diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs index 8032b9d5682c5..421019888a8d6 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs @@ -57,7 +57,7 @@ protected override async Task ExecuteAsync() : null; if (_workItemQueue.TryTakeAnyWork( - preferableProjectId, Processor.DependencyGraph, Processor.DiagnosticAnalyzerService, + preferableProjectId, Processor.DependencyGraph, out var workItem, out var projectCancellation)) { await ProcessProjectAsync(Analyzers, workItem, projectCancellation).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index c51ea2f3c1879..4c62cdeb84f52 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -127,7 +127,7 @@ protected override async Task ExecuteAsync() // process one of documents remaining if (!_workItemQueue.TryTakeAnyWork( - _currentProjectProcessing, Processor.DependencyGraph, Processor.DiagnosticAnalyzerService, + _currentProjectProcessing, Processor.DependencyGraph, out var workItem, out var documentCancellation)) { return; diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs index 2388864694e8e..84aac4e38bdaf 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs @@ -57,7 +57,7 @@ void RegisterRefactoring(MergeDirection direction, TextSpan upperIfOrElseIfSpan, c => RefactorAsync(document, upperIfOrElseIfSpan, lowerIfOrElseIfSpan, c), direction, syntaxFacts.GetText(syntaxKinds.IfKeyword)), - new TextSpan(upperIfOrElseIfSpan.Start, lowerIfOrElseIfSpan.End)); + TextSpan.FromBounds(upperIfOrElseIfSpan.Start, lowerIfOrElseIfSpan.End)); } } } diff --git a/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesService.cs b/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesService.cs index 87326eb2b4443..f2e1c1a414cea 100644 --- a/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesService.cs +++ b/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesService.cs @@ -112,7 +112,6 @@ internal abstract class AbstractSyncNamespacesService action ??= a, options, - isBlocking: false, cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 610091e7fac00..618b03f18a1dc 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -100,11 +100,6 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Přidání {0} do třídy s explicitním nebo sekvenčním rozložením vyžaduje restartování aplikace. - - Adding {0} into a generic type requires restarting the application. - Přidání {0} do obecného typu vyžaduje restartování aplikace. - - Adding {0} into an interface method requires restarting the application. Přidání {0} do metody rozhraní vyžaduje restartování aplikace. @@ -140,11 +135,6 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Přidání konstruktoru k typu s inicializátorem pole nebo vlastnosti, který obsahuje anonymní funkci, vyžaduje restartování aplikace. - - Adding a generic {0} requires restarting the application. - Přidání obecného {0} vyžaduje restartování aplikace. - - Adding a method with an explicit interface specifier requires restarting the application. Přidání metody s explicitním specifikátorem rozhraní vyžaduje restartování aplikace. @@ -1200,21 +1190,6 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Úprava zdroje s povolenými experimentálními jazykovými funkcemi vyžaduje restartování aplikace. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Úprava inicializátoru {0} v obecném typu vyžaduje restartování aplikace. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Úprava prázdných znaků nebo komentářů v {0} uvnitř kontextu obecného typu vyžaduje restartování aplikace. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Úprava prázdných znaků nebo komentářů v obecném {0} vyžaduje restartování aplikace. - - Move contents to namespace... Přesunout obsah do oboru názvů... @@ -2830,6 +2805,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Aktualizace {0} vyžaduje restartování aplikace. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Aktualizace {0} kolem aktivního příkazu vyžaduje restartování aplikace. @@ -2850,6 +2830,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Aktualizace modifikátoru async nebo iterator kolem aktivního příkazu vyžaduje restartování aplikace. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Aktualizace typu pro opětovné načtení (označeného {0}) nebo jeho člena vyžaduje restartování aplikace, protože tuto akci modul runtime nepodporuje. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 1e7b8c23e2454..ccf30680f7fb9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -100,11 +100,6 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Das Hinzufügen von {0} zu einer Klasse mit explizitem oder sequenziellem Layout erfordert einen Neustart der Anwendung. - - Adding {0} into a generic type requires restarting the application. - Das Hinzufügen von {0} zu einem generischen Typ erfordert einen Neustart der Anwendung. - - Adding {0} into an interface method requires restarting the application. Das Hinzufügen von {0} zu einer Schnittstellenmethode erfordert einen Neustart der Anwendung. @@ -140,11 +135,6 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Das Hinzufügen eines Konstruktors zu einem Typ mit einem Feld- oder Eigenschafteninitialisierer, der eine anonyme Funktion enthält, erfordert einen Neustart der Anwendung. - - Adding a generic {0} requires restarting the application. - Das Hinzufügen einer generischen {0}erfordert einen Neustart der Anwendung. - - Adding a method with an explicit interface specifier requires restarting the application. Das Hinzufügen einer Methode mit einem expliziten Schnittstellenbezeichner erfordert einen Neustart der Anwendung. @@ -1200,21 +1190,6 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Das Ändern der Quelle mit aktivierten experimentellen Sprachfeatures erfordert einen Neustart der Anwendung. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Das Aktualisieren des Initialisierers von {0} in einen generischen Typ erfordert einen Neustart der Anwendung. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Das Ändern von Leerzeichen oder Kommentaren in {0} im Kontext eines generischen Typs erfordert einen Neustart der Anwendung. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Das Ändern von Leerzeichen oder Kommentaren in einer generischen {0} erfordert einen Neustart der Anwendung. - - Move contents to namespace... Inhalt in Namespace verschieben... @@ -2830,6 +2805,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Das Aktualisieren von „{0}“ erfordert einen Neustart der Anwendung. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Das Aktualisieren einer {0} um eine aktive Anweisung erfordert einen Neustart der Anwendung. @@ -2850,6 +2830,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Das Aktualisieren eines asynchronen oder iteratormodifizierers um eine aktive Anweisung erfordert einen Neustart der Anwendung. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Das Aktualisieren eines aufladbaren Typs (gekennzeichnet durch {0}) oder seines Elements erfordert einen Neustart der Anwendung, da es von der Laufzeit nicht unterstützt wird. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 6d74a5638aa25..b1cf85449f9fa 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -100,11 +100,6 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Para agregar {0} en una clase con un diseño secuencial o explícito es necesario reiniciar la aplicación. - - Adding {0} into a generic type requires restarting the application. - Para agregar {0} a un tipo genérico, es necesario reiniciar la aplicación. - - Adding {0} into an interface method requires restarting the application. Para agregar {0} en un método de interfaz es necesario reiniciar la aplicación. @@ -140,11 +135,6 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Para agregar un constructor a un tipo con un inicializador de campo o propiedad que contiene una función anónima, es necesario reiniciar la aplicación. - - Adding a generic {0} requires restarting the application. - Para agregar un {0} genérico, es necesario reiniciar la aplicación. - - Adding a method with an explicit interface specifier requires restarting the application. Para agregar un método con un especificador de interfaz explícito, es necesario reiniciar la aplicación. @@ -1200,21 +1190,6 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Para modificar el origen con las características de lenguaje experimental habilitadas se requiere reiniciar la aplicación. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Para modificar el inicializador de {0} en un tipo genérico se requiere reiniciar la aplicación. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Para modificar espacios en blanco o comentarios en {0} dentro del contexto de un tipo genérico se requiere reiniciar la aplicación. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Para modificar espacios en blanco o comentarios en un {0}genérico es necesario reiniciar la aplicación. - - Move contents to namespace... Mover contenido al espacio de nombres... @@ -2830,6 +2805,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Para actualizar "{0}" es necesario reiniciar la aplicación. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Para actualizar un {0} alrededor de una instrucción de acción, es necesario reiniciar la aplicación. @@ -2850,6 +2830,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us La actualización del modificador asincrónico async o iterador iterator alrededor de una instrucción de acción requiere reiniciar la aplicación. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Para actualizar un tipo recargable (marcado por {0}) o su miembro, se requiere reiniciar la aplicación porque no es compatible con el runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index d2ee2979cf5e8..391fb91300d67 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -100,11 +100,6 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai L’ajout de {0} dans une classe avec une disposition explicite ou séquentielle requiert le redémarrage de l’application. - - Adding {0} into a generic type requires restarting the application. - L’ajout de {0} dans un type générique requiert le redémarrage de l’application. - - Adding {0} into an interface method requires restarting the application. L’ajout de {0} dans une méthode d’interface requiert le redémarrage de l’application. @@ -140,11 +135,6 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai L'ajout d'un constructeur à un type avec un initialiseur de champ ou de propriété contenant une fonction anonyme requiert le redémarrage de l’application. - - Adding a generic {0} requires restarting the application. - L’ajout d’un {0} générique requiert le redémarrage de l’application. - - Adding a method with an explicit interface specifier requires restarting the application. L’ajout d’une méthode avec un spécificateur d’interface explicite requiert le redémarrage de l’application. @@ -1200,21 +1190,6 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai La modification de la source avec les fonctionnalités de langage expérimentales activées requiert le redémarrage de l’application. - - Modifying the initializer of {0} in a generic type requires restarting the application. - La modification de l’initialiseur de {0} dans un type générique requiert le redémarrage de l’application. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - La modification d’espaces blancs ou de commentaires dans {0} dans le contexte d’un type générique requiert le redémarrage de l’application. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - La modification des espaces blancs ou des commentaires dans une {0} générique requiert le redémarrage de l’application. - - Move contents to namespace... Déplacer le contenu vers un espace de noms... @@ -2830,6 +2805,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée La mise à jour de « {0} » requiert le redémarrage de l’application. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. La mise à jour d’un {0} autour d’une instruction active requiert le redémarrage de l’application. @@ -2850,6 +2830,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée La mise à jour d'un modificateur async ou iterator autour d'une déclaration active requiert le redémarrage de l'application. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. La mise à jour d’un type rechargeable (marqué par {0}) ou de son membre requiert le redémarrage de l’application, car il n’est pas pris en charge par le runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index f55b4916436ab..77092efad4ec7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -100,11 +100,6 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se si aggiunge {0} in una classe con layout esplicito o sequenziale, è necessario riavviare l'applicazione. - - Adding {0} into a generic type requires restarting the application. - Se si aggiunge {0} in un tipo generico, è necessario riavviare l'applicazione. - - Adding {0} into an interface method requires restarting the application. Se si aggiunge {0} in un metodo di interfaccia, è necessario riavviare l'applicazione. @@ -140,11 +135,6 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se aggiunge un costruttore a un tipo con un inizializzatore di campo o proprietà che contiene una funzione anonima, è necessario riavviare l'applicazione. - - Adding a generic {0} requires restarting the application. - Se si aggiunge un elemento {0} generico, è necessario riavviare l'applicazione. - - Adding a method with an explicit interface specifier requires restarting the application. Se si aggiunge un metodo con un identificatore di interfaccia esplicito, è necessario riavviare l'applicazione. @@ -1200,21 +1190,6 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se si modifica l'origine con le funzionalità del linguaggio sperimentali abilitate, è necessario riavviare l'applicazione. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Se si modifica l'inizializzatore di {0} in un tipo generico, è necessario riavviare l'applicazione. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Se si modificano spazi vuoti o commenti in {0} all'interno del contesto di un tipo generico, è necessario riavviare l'applicazione. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Se si modificano spazi vuoti o commenti in un elemento {0} generico, è necessario riavviare l'applicazione. - - Move contents to namespace... Sposta contenuto nello spazio dei nomi... @@ -2830,6 +2805,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Se si elimina '{0}', è necessario riavviare l'applicazione. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Se si aggiorna un elemento {0} all'inizio di un'istruzione attiva, è necessario riavviare l'applicazione. @@ -2850,6 +2830,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Se si aggiorna il modificatore async o di iteratore in un'istruzione attiva, è necessario riavviare l'applicazione. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Se si carica un tipo ricaricabile (contrassegnato da {0}) o il relativo membro, è necessario riavviare l'applicazione perché il tipo non è supportato dal runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index dc2b63f081953..69f149b7c7d76 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -100,11 +100,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 明示的またはシーケンシャルなレイアウトのクラスに {0} を追加するには、アプリケーションを再起動する必要があります。 - - Adding {0} into a generic type requires restarting the application. - ジェネリック型に {0} を追加するには、アプリケーションを再起動する必要があります。 - - Adding {0} into an interface method requires restarting the application. インターフェイス メソッドに {0} を追加するには、アプリケーションを再起動する必要があります。 @@ -140,11 +135,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 匿名関数を含むフィールドまたはプロパティの初期化子を持つ型にコンストラクターを追加するには、アプリケーションを再起動する必要があります。 - - Adding a generic {0} requires restarting the application. - 汎用 {0} を追加するには、アプリケーションを再起動する必要があります。 - - Adding a method with an explicit interface specifier requires restarting the application. 明示的なインターフェイス指定子を含むメソッドを追加するには、アプリケーションを再起動する必要があります。 @@ -1200,21 +1190,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 試験的な言語機能を有効にしてソースを変更するには、アプリケーションを再起動する必要があります。 - - Modifying the initializer of {0} in a generic type requires restarting the application. - ジェネリック型の {0} の初期化子を変更するには、アプリケーションを再起動する必要があります。 - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - ジェネリック型のコンテキスト内で {0} の空白またはコメントを変更するには、アプリケーションを再起動する必要があります。 - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - ジェネリックの {0} 内の空白またはコメントを変更するには、アプリケーションを再起動する必要があります。 - - Move contents to namespace... 名前空間へのコンテンツの移動... @@ -2830,6 +2805,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' を更新するには、アプリケーションを再起動する必要があります。 + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. アクティブ ステートメントの前後の {0} を更新するには、アプリケーションを再起動する必要があります。 @@ -2850,6 +2830,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of アクティブ ステートメントの前後の async 修飾子または iterator 修飾子を更新するには、アプリケーションを再起動する必要があります。 {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. ランタイムでサポートされていないため、再読み込み可能な型 ({0} とマークされている) またはそのメンバーを更新するには、アプリケーションを再起動する必要があります。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 957569eb776d0..2775ed8c368e9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -100,11 +100,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 명시적 또는 순차 레이아웃이 있는 클래스에 {0}을(를) 추가하려면 응용 프로그램을 다시 시작해야 합니다. - - Adding {0} into a generic type requires restarting the application. - 제네릭 형식에 {0}을(를) 추가하려면 애플리케이션을 다시 시작해야 합니다. - - Adding {0} into an interface method requires restarting the application. 인터페이스 메소드에 {0}을(를) 추가하려면 응용 프로그램을 다시 시작해야 합니다. @@ -140,11 +135,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 익명 함수가 포함된 필드 또는 속성 이니셜라이저가 있는 형식에 생성자를 추가하려면 응용 프로그램을 다시 시작해야 합니다. - - Adding a generic {0} requires restarting the application. - 제네릭 {0}을(를) 추가하려면 애플리케이션을 다시 시작해야 합니다. - - Adding a method with an explicit interface specifier requires restarting the application. 명시적 인터페이스 지정자를 사용하여 메서드를 추가하려면 애플리케이션을 다시 시작해야 합니다. @@ -1200,21 +1190,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 실험 언어 기능이 활성화된 원본를 수정하려면 응용 프로그램을 다시 시작해야 합니다. - - Modifying the initializer of {0} in a generic type requires restarting the application. - 제네릭 형식의 {0}의 이니셜라이저를 수정하려면 애플리케이션을 다시 시작해야 합니다. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - 제네릭 형식의 컨텍스트 내에서 {0}의 공백 또는 주석을 수정하려면 애플리케이션을 다시 시작해야 합니다. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - 제네릭 {0}에서 공백 또는 주석을 수정하려면 애플리케이션을 다시 시작해야 합니다. - - Move contents to namespace... 네임스페이스로 콘텐츠 이동... @@ -2830,6 +2805,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}'을(를) 업데이트하려면 애플리케이션을 다시 시작해야 합니다. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. 활성 문 주변의 {0}을(를) 업데이트하려면 응용 프로그램을 다시 시작해야 합니다. @@ -2850,6 +2830,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 활성 문 주변의 async 또는 iterator 수정자를 업데이트하려면 응용 프로그램을 다시 시작해야 합니다. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. 런타임에서 지원되지 않으므로 다시 로드할 수 있는 형식({0}(으)로 표시) 또는 해당 멤버를 업데이트하려면 애플리케이션을 다시 시작해야 합니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index cf05937eef820..a0cc6e1a217f4 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -100,11 +100,6 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Dodawanie elementu {0} do klasy za pomocą jawnego lub sekwencyjnego układu wymaga ponownego uruchomienia aplikacji. - - Adding {0} into a generic type requires restarting the application. - Dodanie elementu {0} do typu ogólnego wymaga ponownego uruchomienia aplikacji. - - Adding {0} into an interface method requires restarting the application. Dodanie elementu {0} do metody interfejsu wymaga ponownego uruchomienia aplikacji. @@ -140,11 +135,6 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Dodanie konstruktora do typu z inicjatorem pola lub właściwości zawierającego funkcję anonimową wymaga ponownego uruchomienia aplikacji. - - Adding a generic {0} requires restarting the application. - Dodanie ogólnego elementu {0} wymaga ponownego uruchomienia aplikacji. - - Adding a method with an explicit interface specifier requires restarting the application. Dodanie metody z jawnym specyfikatorem interfejsu wymaga ponownego uruchomienia aplikacji. @@ -1200,21 +1190,6 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Modyfikowanie źródła z włączonymi funkcjami eksperymentalnymi języka wymaga ponownego uruchomienia aplikacji. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Modyfikacja inicjatora elementu {0} w typie ogólnym wymaga ponownego uruchomienia aplikacji. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Modyfikacja odstępów lub komentarzy w elemencie {0} w kontekście typu ogólnego wymaga ponownego uruchomienia aplikacji. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Modyfikacja odstępów lub komentarzy w ogólnym elemencie {0} wymaga ponownego uruchomienia aplikacji. - - Move contents to namespace... Przenieś zawartość do przestrzeni nazw... @@ -2830,6 +2805,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Aktualizacja elementu „{0}” wymaga ponownego uruchomienia aplikacji. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Aktualizowanie elementu {0} wokół aktywnej instrukcji wymaga ponownego uruchomienia aplikacji. @@ -2850,6 +2830,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Aktualizowanie modyfikatora asynchronicznego lub modyfikatora moduły iterator wokół aktywnej instrukcji wymaga ponownego uruchomienia aplikacji. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Aktualizacja typu możliwego do załadowania ponownego (oznaczonego przez element {0}) lub jego składowej wymaga ponownego uruchomienia aplikacji, ponieważ nie jest ona obsługiwana przez środowisko uruchomieniowe. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index eb7311fbb031d..d4afac1c24cd9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -100,11 +100,6 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Adicionar {0} a uma classe com layout explícito ou sequencial requer a reinicialização do aplicativo. - - Adding {0} into a generic type requires restarting the application. - Adicionar {0} a um tipo genérico requer o reinício do aplicativo. - - Adding {0} into an interface method requires restarting the application. Adicionar {0} a um método de interface requer a reinicialização do aplicativo. @@ -140,11 +135,6 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Adicionar um construtor a um tipo com um inicializador de campo ou propriedade que contém uma função anônima requer a reinicialização do aplicativo. - - Adding a generic {0} requires restarting the application. - Adicionar um {0} genérico requer o reinício do aplicativo. - - Adding a method with an explicit interface specifier requires restarting the application. Adicionar um método com um especificador de interface explícito requer o reinício do aplicativo. @@ -1200,21 +1190,6 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Modificar a origem com recursos de linguagem experimental habilitados requer a reinicialização do aplicativo. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Modificar o inicializador de {0} em um tipo genérico requer a reinicialização do aplicativo. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - A modificação de espaços em branco ou comentários em {0} dentro do contexto de um tipo genérico requer o reinício do aplicativo. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - A modificação de espaços em branco ou comentários em um {0} genérico requer a reinicialização do aplicativo. - - Move contents to namespace... Mover conteúdo para o namespace... @@ -2830,6 +2805,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Atualizar '{0}' requer reiniciar o aplicativo. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Atualizar um {0} em torno de uma instrução ativa requer a reinicialização do aplicativo. @@ -2850,6 +2830,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Atualizar o modificador async ou iterator em torno de uma instrução ativa requer a reinicialização do aplicativo. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Atualizar um tipo recarregável (marcado por {0}) ou seu membro requer a reinicialização do aplicativo porque não é suportado pelo tempo de execução. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index fd75d8a154969..90a8e079155bb 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -100,11 +100,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Для добавления {0} в класс с явным или последовательным макетом требуется перезапустить приложение. - - Adding {0} into a generic type requires restarting the application. - Для добавления {0} в универсальный тип требуется перезапустить приложение. - - Adding {0} into an interface method requires restarting the application. Для добавления {0} в метод интерфейса требуется перезапустить приложение. @@ -140,11 +135,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Для добавления конструктора к типу с инициализатором поля или свойства, содержащим анонимную функцию, требуется перезапустить приложение. - - Adding a generic {0} requires restarting the application. - Для добавления универсального {0} требуется перезапустить приложение. - - Adding a method with an explicit interface specifier requires restarting the application. Для добавления метода с явным описателем интерфейса требуется перезапустить приложение. @@ -1200,21 +1190,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Для изменения исходного кода с включенными экспериментальными функциями языка требуется перезапустить приложение.. - - Modifying the initializer of {0} in a generic type requires restarting the application. - Для изменения инициализатора {0} универсального типа требуется перезапустить приложение. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Для изменения пустого пространства или комментариев в {0} в контексте универсального типа требуется перезапустить приложение. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Для изменения пустого пространства или комментариев в универсальном {0} требуется перезапустить приложение. - - Move contents to namespace... Переместить содержимое в пространство имен... @@ -2830,6 +2805,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Для обновления "{0}" требуется перезапустить приложение. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Для обновления {0} вокруг активного оператора требуется перезапустить приложение. @@ -2850,6 +2830,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Для обновления модификатора async или iterator вокруг активного оператора требуется перезапустить приложение. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Для обновления перезагружаемого типа (отмеченного атрибутом {0}) или его элемента требуется перезапустить приложение, так как обновление не поддерживается средой выполнения. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index a277621165a21..a890064d01044 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -100,11 +100,6 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Açık veya sıralı düzene sahip bir sınıfa {0} eklemek, uygulamanın yeniden başlatılmasını gerektirir. - - Adding {0} into a generic type requires restarting the application. - Genel bir türe {0} eklenmesi, uygulamanın yeniden başlatılmasını gerektirir. - - Adding {0} into an interface method requires restarting the application. Bir arabirim yöntemine {0} eklemek, uygulamanın yeniden başlatılmasını gerektirir. @@ -140,11 +135,6 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Anonim bir tür içeren alan veya özellik başlatıcısının bulunduğu bir türe oluşturucu eklenmesi, uygulamanın yeniden başlatılmasını gerektirir. - - Adding a generic {0} requires restarting the application. - Genel bir {0} eklemek için uygulamanın yeniden başlatılması gerekir. - - Adding a method with an explicit interface specifier requires restarting the application. Açık bir arabirim tanımlayıcısıyla bir yöntem eklemek, uygulamanın yeniden başlatılmasını gerektirir. @@ -1200,21 +1190,6 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Deneysel dil özellikleri etkinken kaynağı değiştirme, uygulamanın yeniden başlatılmasını gerektirir. - - Modifying the initializer of {0} in a generic type requires restarting the application. - {0} öğesinin başlatıcısının genel bir türde değiştirilmesi, uygulamanın yeniden başlatılmasını gerektirir. - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - Genel bir tür bağlamında {0} öğesindeki boşlukların veya yorumların değiştirilmesi, uygulamanın yeniden başlatılmasını gerektirir. - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - Genel bir {0} öğesindeki boşlukların veya yorumları değiştirilmesi, uygulamanın yeniden başlatılmasını gerektirir. - - Move contents to namespace... İçerikleri ad alanına taşı... @@ -2830,6 +2805,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri {0} öğesinin güncelleştirilmesi, uygulamanın yeniden başlatılmasını gerektirir. + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. Etkin bir deyim etrafında bir {0} güncelleştirmesi, uygulamanın yeniden başlatılmasını gerektirir. @@ -2850,6 +2830,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri Etkin bir deyim etrafında async veya iterator değiştiricisini güncelleştirmek uygulamanın yeniden başlatılmasını gerektirir. {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. Yeniden yüklenebilir bir türün ({0} tarafından işaretlenen) veya üyenin güncelleştirilmesi, çalışma zamanı tarafından desteklenmediğinden uygulamanın yeniden başlatılmasını gerektirir. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index abf437cd09a74..f20d3350000a9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -100,11 +100,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 将 {0} 添加到具有显式或顺序布局的类中需要重新启动应用程序。 - - Adding {0} into a generic type requires restarting the application. - 向泛型类型添加 {0} 需要重启应用程序。 - - Adding {0} into an interface method requires restarting the application. 将 {0} 添加到接口方法中需要重新启动应用程序。 @@ -140,11 +135,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 使用包含匿名类型的字段或属性初始值设定项向类型添加构造函数需要重新启动应用程序。 - - Adding a generic {0} requires restarting the application. - 添加泛型 {0} 要求重启应用程序。 - - Adding a method with an explicit interface specifier requires restarting the application. 添加具有显式接口说明符的方法需要重启应用程序。 @@ -1200,21 +1190,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 在启用实验语言功能的情况下修改源需要重新启动应用程序。 - - Modifying the initializer of {0} in a generic type requires restarting the application. - 在泛型类型中修改 {0} 的初始化表达式需要重启应用程序。 - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - 修改泛型类型上下文内 {0} 中空格或注释需要重启应用程序。 - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - 修改泛型 {0} 中的空格或注释需要重启应用程序。 - - Move contents to namespace... 将内容移动到命名空间... @@ -2830,6 +2805,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新“{0}”需要重启应用程序。 + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. 更新活动语句周围的 {0} 需要重新启动应用程序。 @@ -2850,6 +2830,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新活动语句周围的 async 或 iterator 修饰符需要重新启动应用程序。 {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. 更新可重载类型(由 {0} 标记)或其成员需要重启应用程序,因为运行时不支持。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 3aa02ddd7e74e..5120a7b6c0e3e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -100,11 +100,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 在具有明確或循序配置的類別中新增 {0} 需要重新啟動應用程式。 - - Adding {0} into a generic type requires restarting the application. - 在泛型型別中新增 {0} 需要重新啟動應用程式。 - - Adding {0} into an interface method requires restarting the application. 在介面方法中新增 {0} 需要重新啟動應用程式。 @@ -140,11 +135,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 使用包含匿名函式的欄位或屬性初始設定式,將建構函式新增至類型需要重新啟動應用程式。 - - Adding a generic {0} requires restarting the application. - 新增一般 {0} 需要重新啟動應用程式。 - - Adding a method with an explicit interface specifier requires restarting the application. 新增具有明確介面指定名稱的方法需要重新啟動應用程式。 @@ -1200,21 +1190,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 以啟用的實驗語言功能修改原始檔,需要重新啟動應用程式。 - - Modifying the initializer of {0} in a generic type requires restarting the application. - 修改泛型型別中 {0} 的初始設定式需要重新啟動應用程式。 - - - - Modifying whitespace or comments in {0} inside the context of a generic type requires restarting the application. - 修改泛型型別内容 {0} 中的空白或註解需要重新啟動應用程式。 - - - - Modifying whitespace or comments in a generic {0} requires restarting the application. - 修改泛型 {0} 中的空白或註解需要重新啟動應用程式。 - - Move contents to namespace... 將內容移到命名空間... @@ -2830,6 +2805,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新 '{0}' 需要重新啟動應用程式。 + + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + Updating {0} within generic type or method requires restarting the application because is not supported by the runtime. + + Updating a {0} around an active statement requires restarting the application. 更新作用中陳述式前後的 {0} 需要重新啟動應用程式。 @@ -2850,6 +2830,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新作用中陳述式前後的 async 或 iterator 修飾元需要重新啟動應用程式。 {Locked="async"}{Locked="iterator"} "async" and "iterator" are C#/VB keywords and should not be localized. + + Updating async or iterator requires restarting the application because is not supported by the runtime. + Updating async or iterator requires restarting the application because is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member requires restarting the application because is not supported by the runtime. 因為執行階段不支援更新可重新載入類型 (由 {0} 標記) 或其成員,所以需要重新啟動應用程式。 diff --git a/src/Features/DiagnosticsTestUtilities/Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities.csproj b/src/Features/DiagnosticsTestUtilities/Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities.csproj new file mode 100644 index 0000000000000..fdab2cfd71ae4 --- /dev/null +++ b/src/Features/DiagnosticsTestUtilities/Microsoft.CodeAnalysis.Features.DiagnosticsTests.Utilities.csproj @@ -0,0 +1,70 @@ + + + + + Library + Microsoft.CodeAnalysis.Test.Utilities + net472 + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + global,WORKSPACES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs index 04e276fcc57ec..d4ec2d69407ef 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/HandlerProvider.cs @@ -142,28 +142,20 @@ static string GetRequestHandlerMethod(Type handlerType, Type? requestType, Type static LanguageServerEndpointAttribute? GetMethodAttributeFromHandlerMethod(Type handlerType, Type? requestType, Type contextType, Type? responseType) { - MethodInfo? methodInfo; - if (requestType is not null && responseType is not null) + var methodInfo = (requestType != null, responseType != null) switch { - methodInfo = handlerType.GetMethod(nameof(IRequestHandler.HandleRequestAsync), new Type[] { requestType, contextType, typeof(CancellationToken) }); - } - else if (requestType is not null) - { - methodInfo = handlerType.GetMethod(nameof(INotificationHandler.HandleNotificationAsync), new Type[] { requestType, contextType, typeof(CancellationToken) }); - } - else - { - methodInfo = handlerType.GetMethod(nameof(INotificationHandler.HandleNotificationAsync), new Type[] { contextType, typeof(CancellationToken) }); - } + (true, true) => handlerType.GetMethod(nameof(IRequestHandler.HandleRequestAsync), new Type[] { requestType!, contextType, typeof(CancellationToken) }), + (false, true) => handlerType.GetMethod(nameof(IRequestHandler.HandleRequestAsync), new Type[] { contextType, typeof(CancellationToken) }), + (true, false) => handlerType.GetMethod(nameof(INotificationHandler.HandleNotificationAsync), new Type[] { requestType!, contextType, typeof(CancellationToken) }), + (false, false) => handlerType.GetMethod(nameof(INotificationHandler.HandleNotificationAsync), new Type[] { contextType, typeof(CancellationToken) }) + }; if (methodInfo is null) { throw new InvalidOperationException("Somehow we are missing the method for our registered handler"); } - var attribute = methodInfo.GetCustomAttribute(); - - return attribute; + return methodInfo.GetCustomAttribute(); } static LanguageServerEndpointAttribute? GetMethodAttributeFromClassOrInterface(Type type) diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs index 4ee3ad72b9d4f..cbdff1e945242 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/IRequestHandler.cs @@ -20,3 +20,14 @@ public interface IRequestHandler : IMethod /// The LSP response. Task HandleRequestAsync(TRequest request, TRequestContext context, CancellationToken cancellationToken); } + +public interface IRequestHandler : IMethodHandler +{ + /// + /// Handles an LSP request in the context of the supplied document and/or solution. + /// + /// The LSP request context, which should have been filled in with document information from if applicable. + /// A cancellation token that can be used to cancel the request processing. + /// The LSP response. + Task HandleRequestAsync(TRequestContext context, CancellationToken cancellationToken); +} diff --git a/src/Features/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs b/src/Features/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs index a115d86918e49..803821542f104 100644 --- a/src/Features/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs +++ b/src/Features/LanguageServer/Protocol/DefaultCapabilitiesProvider.cs @@ -44,7 +44,7 @@ public void Initialize() public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) { - var supportsVsExtensions = clientCapabilities is VSInternalClientCapabilities { SupportsVisualStudioExtensions: true }; + var supportsVsExtensions = clientCapabilities.HasVisualStudioLspCapability(); var capabilities = supportsVsExtensions ? GetVSServerCapabilities() : new ServerCapabilities(); var commitCharacters = CompletionRules.Default.DefaultCommitCharacters.Select(c => c.ToString()).ToArray(); @@ -83,17 +83,17 @@ public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) capabilities.HoverProvider = true; - // Using only range handling has shown to be more performant than using a combination of full/edits/range handling, - // especially for larger files. With range handling, we only need to compute tokens for whatever is in view, while - // with full/edits handling we need to compute tokens for the entire file and then potentially run a diff between - // the old and new tokens. + // Using only range handling has shown to be more performant than using a combination of full/edits/range + // handling, especially for larger files. With range handling, we only need to compute tokens for whatever + // is in view, while with full/edits handling we need to compute tokens for the entire file and then + // potentially run a diff between the old and new tokens. capabilities.SemanticTokensOptions = new SemanticTokensOptions { Full = false, Range = true, Legend = new SemanticTokensLegend { - TokenTypes = SemanticTokenTypes.AllTypes.Concat(SemanticTokensHelpers.RoslynCustomTokenTypes).ToArray(), + TokenTypes = SemanticTokensSchema.GetSchema(clientCapabilities).AllTokenTypes.ToArray(), TokenModifiers = new string[] { SemanticTokenModifiers.Static } } }; diff --git a/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs b/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs index 42e5d7d8b6210..0f2cf01c36cec 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs @@ -5,12 +5,11 @@ using System; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; -using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindUsages; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -30,6 +29,22 @@ public static Uri GetURI(this TextDocument document) : ProtocolConversions.GetUriFromFilePath(document.FilePath); } + /// + /// Generate the Uri of a document by replace the name in file path using the document's name. + /// Used to generate the correct Uri when rename a document, because calling doesn't update the file path. + /// + public static Uri GetUriForRenamedDocument(this TextDocument document) + { + Contract.ThrowIfNull(document.FilePath); + Contract.ThrowIfNull(document.Name); + Contract.ThrowIfTrue(document is SourceGeneratedDocument); + var directoryName = Path.GetDirectoryName(document.FilePath); + + Contract.ThrowIfNull(directoryName); + var path = Path.Combine(directoryName, document.Name); + return ProtocolConversions.GetUriFromFilePath(path); + } + public static Uri? TryGetURI(this TextDocument document, RequestContext? context = null) => ProtocolConversions.TryGetUriFromFilePath(document.FilePath, context); diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index ca22bf290e309..7ce93517e5161 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -205,13 +205,27 @@ public static TextSpan RangeToTextSpan(LSP.Range range, SourceText text) try { - return text.Lines.GetTextSpan(linePositionSpan); + try + { + return text.Lines.GetTextSpan(linePositionSpan); + } + catch (ArgumentException) + { + // Create a custom error for this so we can examine the data we're getting. + throw new ArgumentException($"Range={RangeToString(range)}. text.Length={text.Length}. text.Lines.Count={text.Lines.Count}"); + } } // Temporary exception reporting to investigate https://github.com/dotnet/roslyn/issues/66258. catch (Exception e) when (FatalError.ReportAndPropagate(e)) { throw; } + + static string RangeToString(LSP.Range range) + => $"{{ Start={PositionToString(range.Start)}, End={PositionToString(range.End)} }}"; + + static string PositionToString(LSP.Position position) + => $"{{ Line={position.Line}, Character={position.Character} }}"; } public static LSP.TextEdit TextChangeToTextEdit(TextChange textChange, SourceText oldText) diff --git a/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 0a6a59049fc1b..62d2d5e494b5c 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -200,7 +200,9 @@ private async Task> GetThir var diagnostics = (await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, shouldIncludeDiagnostic: static diagnosticId => !(IDEDiagnosticIdToOptionMappingHelper.IsKnownIDEDiagnosticId(diagnosticId)), includeCompilerDiagnostics: true, includeSuppressedDiagnostics: false, - cancellationToken: cancellationToken).ConfigureAwait(false)); + priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + addOperationScope: null, DiagnosticKind.All, isExplicit: false, + cancellationToken).ConfigureAwait(false)); // ensure more than just known diagnostics were returned if (!diagnostics.Any()) diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index 6282aadbfbbac..b06b8fb3783d1 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -45,7 +46,7 @@ public FixAllDiagnosticProvider(IDiagnosticAnalyzerService diagnosticService, Im public override async Task> GetDocumentDiagnosticsAsync(Document document, CancellationToken cancellationToken) { var solution = document.Project.Solution; - var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(solution, null, document.Id, _diagnosticIds, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(solution, projectId: null, document.Id, _diagnosticIds, _includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics: false, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } @@ -53,7 +54,9 @@ public override async Task> GetDocumentDiagnosticsAsync( public override async Task> GetDocumentSpanDiagnosticsAsync(Document document, TextSpan fixAllSpan, CancellationToken cancellationToken) { bool shouldIncludeDiagnostic(string id) => _diagnosticIds == null || _diagnosticIds.Contains(id); - var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync(document, fixAllSpan, shouldIncludeDiagnostic, _includeSuppressedDiagnostics, cancellationToken: cancellationToken).ConfigureAwait(false); + var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync(document, fixAllSpan, shouldIncludeDiagnostic, + includeCompilerDiagnostics: true, _includeSuppressedDiagnostics, priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + addOperationScope: null, DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(diagnostics.All(d => d.DocumentId != null)); return await diagnostics.ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); } diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index eb00696c4f51b..13021294f212e 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -73,7 +73,7 @@ internal partial class CodeFixService : ICodeFixService private Func? GetShouldIncludeDiagnosticPredicate( TextDocument document, - CodeActionRequestPriority priority) + ICodeActionRequestPriorityProvider priorityProvider) { // For Normal or Low priority, we only need to execute analyzers which can report at least one fixable // diagnostic that can have a non-suppression/configuration fix. @@ -81,7 +81,7 @@ internal partial class CodeFixService : ICodeFixService // For CodeActionPriorityRequest.High, we only run compiler analyzer, which always has fixable diagnostics, // so we can return a null predicate here to include all diagnostics. - if (!(priority is CodeActionRequestPriority.Normal or CodeActionRequestPriority.Low)) + if (!(priorityProvider.Priority is CodeActionRequestPriority.Normal or CodeActionRequestPriority.Low)) return null; var hasWorkspaceFixers = TryGetWorkspaceFixersMap(document, out var workspaceFixersMap); @@ -97,11 +97,11 @@ internal partial class CodeFixService : ICodeFixService } public async Task GetMostSevereFixAsync( - TextDocument document, TextSpan range, CodeActionRequestPriority priority, CodeActionOptionsProvider fallbackOptions, bool isBlocking, CancellationToken cancellationToken) + TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) { var (allDiagnostics, upToDate) = await _diagnosticService.TryGetDiagnosticsForSpanAsync( - document, range, GetShouldIncludeDiagnosticPredicate(document, priority), - includeSuppressedDiagnostics: false, priority, cancellationToken: cancellationToken).ConfigureAwait(false); + document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), + includeSuppressedDiagnostics: false, priorityProvider, DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false); var buildOnlyDiagnosticsService = document.Project.Solution.Services.GetRequiredService(); allDiagnostics.AddRange(buildOnlyDiagnosticsService.GetBuildOnlyDiagnostics(document.Id)); @@ -144,7 +144,7 @@ internal partial class CodeFixService : ICodeFixService { await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, - priority, fallbackOptions, isBlocking, _ => null, cancellationToken).ConfigureAwait(false)) + priorityProvider, fallbackOptions, _ => null, cancellationToken).ConfigureAwait(false)) { // Stop at the result error we see. return collection; @@ -157,15 +157,14 @@ internal partial class CodeFixService : ICodeFixService public async IAsyncEnumerable StreamFixesAsync( TextDocument document, TextSpan range, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, - bool isBlocking, Func addOperationScope, [EnumeratorCancellation] CancellationToken cancellationToken) { // We only need to compute suppression/configuration fixes when request priority is // 'CodeActionPriorityRequest.Lowest' or 'CodeActionPriorityRequest.None'. - var includeSuppressionFixes = priority is CodeActionRequestPriority.Lowest or CodeActionRequestPriority.None; + var includeSuppressionFixes = priorityProvider.Priority is CodeActionRequestPriority.Lowest or CodeActionRequestPriority.None; // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic // service to give back current diagnostics for the given span, and it will use that to get fixes. @@ -178,9 +177,9 @@ internal partial class CodeFixService : ICodeFixService // We mark requests to GetDiagnosticsForSpanAsync as 'isExplicit = true' to indicate // user-invoked diagnostic requests, for example, user invoked Ctrl + Dot operation for lightbulb. var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( - document, range, GetShouldIncludeDiagnosticPredicate(document, priority), - includeCompilerDiagnostics: true, includeSuppressedDiagnostics: includeSuppressionFixes, priority: priority, - addOperationScope: addOperationScope, isExplicit: true, cancellationToken: cancellationToken).ConfigureAwait(false); + document, range, GetShouldIncludeDiagnosticPredicate(document, priorityProvider), + includeCompilerDiagnostics: true, includeSuppressedDiagnostics: includeSuppressionFixes, priorityProvider, + addOperationScope, DiagnosticKind.All, isExplicit: true, cancellationToken).ConfigureAwait(false); var buildOnlyDiagnosticsService = document.Project.Solution.Services.GetRequiredService(); var buildOnlyDiagnostics = buildOnlyDiagnosticsService.GetBuildOnlyDiagnostics(document.Id); @@ -194,11 +193,11 @@ internal partial class CodeFixService : ICodeFixService var spanToDiagnostics = ConvertToMap(text, diagnostics); // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. - if (priority != CodeActionRequestPriority.Lowest) + if (priorityProvider.Priority != CodeActionRequestPriority.Lowest) { await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, - priority, fallbackOptions, isBlocking, addOperationScope, cancellationToken).ConfigureAwait(false)) + priorityProvider, fallbackOptions, addOperationScope, cancellationToken).ConfigureAwait(false)) { yield return collection; } @@ -260,7 +259,8 @@ internal partial class CodeFixService : ICodeFixService cancellationToken.ThrowIfCancellationRequested(); var diagnostics = await _diagnosticService.GetDiagnosticsForSpanAsync( - document, range, diagnosticId, includeSuppressedDiagnostics: false, cancellationToken: cancellationToken).ConfigureAwait(false); + document, range, diagnosticId, includeSuppressedDiagnostics: false, priorityProvider: new DefaultCodeActionRequestPriorityProvider(), + addOperationScope: null, DiagnosticKind.All, isExplicit: false, cancellationToken).ConfigureAwait(false); diagnostics = diagnostics.WhereAsArray(d => d.Severity.IsMoreSevereThanOrEqualTo(minimumSeverity)); if (!diagnostics.Any()) return null; @@ -272,8 +272,8 @@ internal partial class CodeFixService : ICodeFixService }; await foreach (var collection in StreamFixesAsync( - document, spanToDiagnostics, fixAllForInSpan: true, CodeActionRequestPriority.None, - fallbackOptions, isBlocking: false, addOperationScope: static _ => null, cancellationToken).ConfigureAwait(false)) + document, spanToDiagnostics, fixAllForInSpan: true, new DefaultCodeActionRequestPriorityProvider(), + fallbackOptions, addOperationScope: static _ => null, cancellationToken).ConfigureAwait(false)) { if (collection.FixAllState is not null && collection.SupportedScopes.Contains(FixAllScope.Document)) { @@ -402,9 +402,8 @@ private bool TryGetWorkspaceFixersPriorityMap(TextDocument document, [NotNullWhe TextDocument document, SortedDictionary> spanToDiagnostics, bool fixAllForInSpan, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, - bool isBlocking, Func addOperationScope, [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -478,7 +477,7 @@ private bool TryGetWorkspaceFixersPriorityMap(TextDocument document, [NotNullWhe { cancellationToken.ThrowIfCancellationRequested(); - if (priority != CodeActionRequestPriority.None && priority != fixer.RequestPriority) + if (!priorityProvider.MatchesPriority(fixer)) continue; foreach (var (span, diagnostics) in fixerToRangesAndDiagnostics[fixer]) @@ -497,13 +496,13 @@ private bool TryGetWorkspaceFixersPriorityMap(TextDocument document, [NotNullWhe if (fixAllForInSpan) { var primaryDiagnostic = dxs.First(); - return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, fixerMetadata, fallbackOptions, isBlocking, + return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, fixerMetadata, fallbackOptions, ImmutableArray.Create(primaryDiagnostic), uniqueDiagosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); } else { - return GetCodeFixesAsync(document, span, fixer, fixerMetadata, fallbackOptions, isBlocking, dxs, + return GetCodeFixesAsync(document, span, fixer, fixerMetadata, fallbackOptions, dxs, uniqueDiagosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); } } @@ -567,7 +566,7 @@ private bool TryGetWorkspaceFixersPriorityMap(TextDocument document, [NotNullWhe } private static async Task> GetCodeFixesAsync( - TextDocument document, TextSpan span, CodeFixProvider fixer, CodeChangeProviderMetadata? fixerMetadata, CodeActionOptionsProvider fallbackOptions, bool isBlocking, + TextDocument document, TextSpan span, CodeFixProvider fixer, CodeChangeProviderMetadata? fixerMetadata, CodeActionOptionsProvider fallbackOptions, ImmutableArray diagnostics, Dictionary> uniqueDiagosticToEquivalenceKeysMap, Dictionary<(Diagnostic diagnostic, string? equivalenceKey), CodeFixProvider> diagnosticAndEquivalenceKeyToFixersMap, @@ -599,7 +598,6 @@ private bool TryGetWorkspaceFixersPriorityMap(TextDocument document, [NotNullWhe } }, fallbackOptions, - isBlocking, cancellationToken); var task = fixer.RegisterCodeFixesAsync(context) ?? Task.CompletedTask; diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs index 3dbab9dcc1673..b595aefd21d5a 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs @@ -16,14 +16,14 @@ namespace Microsoft.CodeAnalysis.CodeFixes { internal interface ICodeFixService { - IAsyncEnumerable StreamFixesAsync(TextDocument document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptionsProvider options, bool isBlocking, Func addOperationScope, CancellationToken cancellationToken); + IAsyncEnumerable StreamFixesAsync(TextDocument document, TextSpan textSpan, ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider options, Func addOperationScope, CancellationToken cancellationToken); /// /// Similar to except that instead of streaming all results, this ends with the /// first. This will also attempt to return a fix for an error first, but will fall back to any fix if that /// does not succeed. /// - Task GetMostSevereFixAsync(TextDocument document, TextSpan range, CodeActionRequestPriority priority, CodeActionOptionsProvider fallbackOptions, bool isBlocking, CancellationToken cancellationToken); + Task GetMostSevereFixAsync(TextDocument document, TextSpan range, ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken); Task GetDocumentFixAllForIdInSpanAsync(TextDocument document, TextSpan textSpan, string diagnosticId, DiagnosticSeverity severity, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken); Task ApplyCodeFixesForSpecificDiagnosticIdAsync(TDocument document, string diagnosticId, DiagnosticSeverity severity, IProgressTracker progressTracker, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) @@ -33,14 +33,14 @@ Task ApplyCodeFixesForSpecificDiagnosticIdAsync(TDocument internal static class ICodeFixServiceExtensions { - public static IAsyncEnumerable StreamFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CodeActionOptionsProvider fallbackOptions, bool isBlocking, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, range, CodeActionRequestPriority.None, fallbackOptions, isBlocking, addOperationScope: _ => null, cancellationToken); + public static IAsyncEnumerable StreamFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, range, new DefaultCodeActionRequestPriorityProvider(), fallbackOptions, addOperationScope: _ => null, cancellationToken); - public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CodeActionOptionsProvider fallbackOptions, bool isBlocking, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, range, fallbackOptions, isBlocking, cancellationToken).ToImmutableArrayAsync(cancellationToken); + public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan range, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, range, fallbackOptions, cancellationToken).ToImmutableArrayAsync(cancellationToken); - public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptionsProvider fallbackOptions, bool isBlocking, Func addOperationScope, CancellationToken cancellationToken) - => service.StreamFixesAsync(document, textSpan, priority, fallbackOptions, isBlocking, addOperationScope, cancellationToken).ToImmutableArrayAsync(cancellationToken); + public static Task> GetFixesAsync(this ICodeFixService service, TextDocument document, TextSpan textSpan, ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, Func addOperationScope, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, textSpan, priorityProvider, fallbackOptions, addOperationScope, cancellationToken).ToImmutableArrayAsync(cancellationToken); public static Task GetDocumentFixAllForIdInSpanAsync(this ICodeFixService service, TextDocument document, TextSpan range, string diagnosticId, CodeActionOptionsProvider fallbackOptions, CancellationToken cancellationToken) => service.GetDocumentFixAllForIdInSpanAsync(document, range, diagnosticId, DiagnosticSeverity.Hidden, fallbackOptions, cancellationToken); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index ec43ed93eb37b..3d76bdd469550 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -59,7 +59,7 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService GlobalOptions = globalOptions; } - public void Reanalyze(Workspace workspace, IEnumerable? projectIds = null, IEnumerable? documentIds = null, bool highPriority = false) + public void Reanalyze(Workspace workspace, IEnumerable? projectIds, IEnumerable? documentIds, bool highPriority) { var service = workspace.Services.GetService(); if (service != null && _map.TryGetValue(workspace, out var analyzer)) @@ -72,11 +72,11 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds = TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, - bool includeSuppressedDiagnostics = false, - CodeActionRequestPriority priority = CodeActionRequestPriority.None, - DiagnosticKind diagnosticKinds = DiagnosticKind.All, - bool isExplicit = false, - CancellationToken cancellationToken = default) + bool includeSuppressedDiagnostics, + ICodeActionRequestPriorityProvider priorityProvider, + DiagnosticKind diagnosticKinds, + bool isExplicit, + CancellationToken cancellationToken) { if (_map.TryGetValue(document.Project.Solution.Workspace, out var analyzer)) { @@ -86,7 +86,7 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds = using var _ = ArrayBuilder.GetInstance(out var diagnostics); var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( document, range, diagnostics, shouldIncludeDiagnostic, - includeSuppressedDiagnostics, true, priority, blockForData: false, + includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); return (diagnostics.ToImmutable(), upToDate); }, cancellationToken); @@ -101,7 +101,7 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds = Func? shouldIncludeDiagnostic, bool includeCompilerDiagnostics, bool includeSuppressedDiagnostics, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, Func? addOperationScope, DiagnosticKind diagnosticKinds, bool isExplicit, @@ -112,13 +112,13 @@ public void Reanalyze(Workspace workspace, IEnumerable? projectIds = // always make sure that analyzer is called on background thread. return Task.Run(() => analyzer.GetDiagnosticsForSpanAsync( document, range, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics, - priority, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken), cancellationToken); + priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken), cancellationToken); } return SpecializedTasks.EmptyImmutableArray(); } - public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId = null, DocumentId? documentId = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(workspace, out var analyzer)) { @@ -128,7 +128,7 @@ public Task> GetCachedDiagnosticsAsync(Workspace return SpecializedTasks.EmptyImmutableArray(); } - public Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetSpecificCachedDiagnosticsAsync(Workspace workspace, object id, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(workspace, out var analyzer)) { @@ -138,7 +138,7 @@ public Task> GetSpecificCachedDiagnosticsAsync(Wo return SpecializedTasks.EmptyImmutableArray(); } - public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(solution.Workspace, out var analyzer)) { @@ -148,7 +148,7 @@ public Task> GetDiagnosticsAsync(Solution solutio return SpecializedTasks.EmptyImmutableArray(); } - public async Task ForceAnalyzeAsync(Solution solution, Action onProjectAnalyzed, ProjectId? projectId = null, CancellationToken cancellationToken = default) + public async Task ForceAnalyzeAsync(Solution solution, Action onProjectAnalyzed, ProjectId? projectId, CancellationToken cancellationToken) { if (_map.TryGetValue(solution.Workspace, out var analyzer)) { @@ -180,7 +180,7 @@ public async Task ForceAnalyzeAsync(Solution solution, Action onProject } public Task> GetDiagnosticsForIdsAsync( - Solution solution, ProjectId? projectId = null, DocumentId? documentId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(solution.Workspace, out var analyzer)) { @@ -191,7 +191,7 @@ public async Task ForceAnalyzeAsync(Solution solution, Action onProject } public Task> GetProjectDiagnosticsForIdsAsync( - Solution solution, ProjectId? projectId = null, ImmutableHashSet? diagnosticIds = null, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (_map.TryGetValue(solution.Workspace, out var analyzer)) { @@ -200,15 +200,5 @@ public async Task ForceAnalyzeAsync(Solution solution, Action onProject return SpecializedTasks.EmptyImmutableArray(); } - - public bool ContainsDiagnostics(Workspace workspace, ProjectId projectId) - { - if (_map.TryGetValue(workspace, out var analyzer)) - { - return analyzer.ContainsDiagnostics(projectId); - } - - return false; - } } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index fd996268a34eb..52f26173d6bcb 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -45,7 +45,7 @@ private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspac } private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) - => Reanalyze(e.Solution.Workspace, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); + => Reanalyze(e.Solution.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); } internal class NoOpIncrementalAnalyzer : IncrementalAnalyzerBase diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ActiveFileState.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ActiveFileState.cs index 0f0c1c9798fd5..80b3249e48739 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ActiveFileState.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ActiveFileState.cs @@ -42,8 +42,8 @@ public void ResetVersion() lock (_gate) { // reset version of cached data so that we can recalculate new data (ex, OnDocumentReset) - _syntax = new DocumentAnalysisData(VersionStamp.Default, _syntax.Items); - _semantic = new DocumentAnalysisData(VersionStamp.Default, _semantic.Items); + _syntax = new DocumentAnalysisData(VersionStamp.Default, _syntax.LineCount, _syntax.Items); + _semantic = new DocumentAnalysisData(VersionStamp.Default, _semantic.LineCount, _semantic.Items); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index e72d4e5ade939..8814eae9d0f6d 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -20,13 +20,18 @@ internal partial class DiagnosticIncrementalAnalyzer /// private readonly struct DocumentAnalysisData { - public static readonly DocumentAnalysisData Empty = new(VersionStamp.Default, ImmutableArray.Empty); + public static readonly DocumentAnalysisData Empty = new(VersionStamp.Default, lineCount: 0, ImmutableArray.Empty); /// /// Version of the diagnostic data. /// public readonly VersionStamp Version; + /// + /// Number of lines in the document. + /// + public readonly int LineCount; + /// /// Current data that matches the version. /// @@ -37,24 +42,25 @@ internal partial class DiagnosticIncrementalAnalyzer /// public readonly ImmutableArray OldItems; - public DocumentAnalysisData(VersionStamp version, ImmutableArray items) + public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray items) { Debug.Assert(!items.IsDefault); Version = version; + LineCount = lineCount; Items = items; OldItems = default; } - public DocumentAnalysisData(VersionStamp version, ImmutableArray oldItems, ImmutableArray newItems) - : this(version, newItems) + public DocumentAnalysisData(VersionStamp version, int lineCount, ImmutableArray oldItems, ImmutableArray newItems) + : this(version, lineCount, newItems) { Debug.Assert(!oldItems.IsDefault); OldItems = oldItems; } public DocumentAnalysisData ToPersistData() - => new(Version, Items); + => new(Version, LineCount, Items); public bool FromCache { 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 8b3b95dd9c6e5..6614c91d79280 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -29,7 +29,7 @@ internal partial class DiagnosticIncrementalAnalyzer /// 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 DocumentAnalysisData? TryGetCachedDocumentAnalysisData( + private (ActiveFileState, DocumentAnalysisData?) TryGetCachedDocumentAnalysisData( TextDocument document, StateSet stateSet, AnalysisKind kind, VersionStamp version, BackgroundAnalysisScope analysisScope, @@ -50,7 +50,7 @@ internal partial class DiagnosticIncrementalAnalyzer if (existingData.Version == version) { - return existingData; + return (state, existingData); } // Check whether analyzer is suppressed for project or document. @@ -60,7 +60,7 @@ internal partial class DiagnosticIncrementalAnalyzer isAnalyzerSuppressed = !DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(stateSet.Analyzer, document.Project, GlobalOptions) || !IsAnalyzerEnabledForDocument(stateSet.Analyzer, existingData, analysisScope, compilerDiagnosticsScope, isActiveDocument, isVisibleDocument, isOpenDocument, isGeneratedRazorDocument); - return null; + return (state, null); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { @@ -130,7 +130,7 @@ internal partial class DiagnosticIncrementalAnalyzer /// Computes all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer). /// private static async Task ComputeDocumentAnalysisDataAsync( - DocumentAnalysisExecutor executor, StateSet stateSet, bool logTelemetry, CancellationToken cancellationToken) + DocumentAnalysisExecutor executor, DiagnosticAnalyzer analyzer, ActiveFileState state, bool logTelemetry, CancellationToken cancellationToken) { var kind = executor.AnalysisScope.Kind; var document = executor.AnalysisScope.TextDocument; @@ -139,22 +139,22 @@ internal partial class DiagnosticIncrementalAnalyzer GetLogFunctionIdAndTitle(kind, out var functionId, out var title); var logLevel = logTelemetry ? LogLevel.Information : LogLevel.Trace; - using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, stateSet.Analyzer, cancellationToken, logLevel: logLevel)) + using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, analyzer, cancellationToken, logLevel: logLevel)) { try { - var diagnostics = await executor.ComputeDiagnosticsAsync(stateSet.Analyzer, cancellationToken).ConfigureAwait(false); + 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, stateSet.Analyzer, diagnostics); + title, document, analyzer, diagnostics); var version = await GetDiagnosticVersionAsync(document.Project, cancellationToken).ConfigureAwait(false); - var state = stateSet.GetOrCreateActiveFileState(document.Id); var existingData = state.GetAnalysisData(kind); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // we only care about local diagnostics - return new DocumentAnalysisData(version, existingData.Items, diagnostics.ToImmutableArrayOrEmpty()); + return new DocumentAnalysisData(version, text.Lines.Count, existingData.Items, diagnostics.ToImmutableArrayOrEmpty()); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken)) { diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 2f2aca5dc352d..128cf10e4622f 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -45,7 +45,7 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) public async Task>> ComputeDiagnosticsAsync( DocumentAnalysisExecutor executor, - ImmutableArray stateSets, + ImmutableArray analyzersWithState, VersionStamp version, Func>> computeAnalyzerDiagnosticsAsync, Func>>> computeDiagnosticsNonIncrementallyAsync, @@ -57,7 +57,7 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) Debug.Assert(!analysisScope.Span.HasValue); // Ensure that only the analyzers that support incremental span-based analysis are provided. - Debug.Assert(stateSets.All(stateSet => stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); + Debug.Assert(analyzersWithState.All(stateSet => stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); var document = (Document)analysisScope.TextDocument; var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -75,29 +75,30 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) { var oldDocumentVersion = await GetDiagnosticVersionAsync(oldDocument.Project, cancellationToken).ConfigureAwait(false); - using var _1 = ArrayBuilder<(DiagnosticAnalyzer, DocumentAnalysisData)>.GetInstance(out var spanBasedAnalyzers); - using var _2 = ArrayBuilder<(DiagnosticAnalyzer, DocumentAnalysisData)>.GetInstance(out var documentBasedAnalyzers); - (DiagnosticAnalyzer analyzer, DocumentAnalysisData existingData, bool spanBased)? compilerAnalyzerData = null; - foreach (var stateSet in stateSets) + using var _1 = ArrayBuilder.GetInstance(out var spanBasedAnalyzers); + using var _2 = ArrayBuilder.GetInstance(out var documentBasedAnalyzers); + (AnalyzerWithState analyzerWithState, bool spanBased)? compilerAnalyzerData = null; + foreach (var analyzerWithState in analyzersWithState) { // Check if we have existing cached diagnostics for this analyzer whose version matches the // old document version. If so, we can perform span based incremental analysis for the changed member. // Otherwise, we have to perform entire document analysis. - var state = stateSet.GetOrCreateActiveFileState(document.Id); - var existingData = state.GetAnalysisData(analysisScope.Kind); + var state = analyzerWithState.State; + var existingData = analyzerWithState.ExistingData; if (oldDocumentVersion == existingData.Version) { - if (!compilerAnalyzerData.HasValue && stateSet.Analyzer.IsCompilerAnalyzer()) - compilerAnalyzerData = (stateSet.Analyzer, existingData, spanBased: true); + if (!compilerAnalyzerData.HasValue && analyzerWithState.Analyzer.IsCompilerAnalyzer()) + compilerAnalyzerData = (analyzerWithState, spanBased: true); else - spanBasedAnalyzers.Add((stateSet.Analyzer, existingData)); + spanBasedAnalyzers.Add(analyzerWithState); } else { - if (!compilerAnalyzerData.HasValue && stateSet.Analyzer.IsCompilerAnalyzer()) - compilerAnalyzerData = (stateSet.Analyzer, DocumentAnalysisData.Empty, spanBased: false); + var analyzerWithStateAndEmptyData = new AnalyzerWithState(analyzerWithState.Analyzer, analyzerWithState.State, DocumentAnalysisData.Empty); + if (!compilerAnalyzerData.HasValue && analyzerWithState.Analyzer.IsCompilerAnalyzer()) + compilerAnalyzerData = (analyzerWithStateAndEmptyData, spanBased: false); else - documentBasedAnalyzers.Add((stateSet.Analyzer, DocumentAnalysisData.Empty)); + documentBasedAnalyzers.Add(analyzerWithStateAndEmptyData); } } @@ -126,47 +127,47 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) } async Task ExecuteCompilerAnalyzerAsync( - (DiagnosticAnalyzer analyzer, DocumentAnalysisData existingData, bool spanBased)? compilerAnalyzerData, + (AnalyzerWithState analyzerWithState, bool spanBased)? compilerAnalyzerData, ImmutableArray oldMemberSpans, PooledDictionary> builder) { if (!compilerAnalyzerData.HasValue) return; - var (analyzer, existingData, spanBased) = compilerAnalyzerData.Value; + var (analyzerWithState, spanBased) = compilerAnalyzerData.Value; var span = spanBased ? changedMember.FullSpan : (TextSpan?)null; executor = executor.With(analysisScope.WithSpan(span)); - var analyzerAndExistingData = SpecializedCollections.SingletonEnumerable((analyzer, existingData)); - await ExecuteAnalyzersAsync(executor, analyzerAndExistingData, oldMemberSpans, builder).ConfigureAwait(false); + using var _ = ArrayBuilder.GetInstance(1, analyzerWithState, out var analyzersWithState); + await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteSpanBasedAnalyzersAsync( - ArrayBuilder<(DiagnosticAnalyzer, DocumentAnalysisData)> analyzersAndExistingData, + ArrayBuilder analyzersWithState, ImmutableArray oldMemberSpans, PooledDictionary> builder) { - if (analyzersAndExistingData.Count == 0) + if (analyzersWithState.Count == 0) return; executor = executor.With(analysisScope.WithSpan(changedMember.FullSpan)); - await ExecuteAnalyzersAsync(executor, analyzersAndExistingData, oldMemberSpans, builder).ConfigureAwait(false); + await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteDocumentBasedAnalyzersAsync( - ArrayBuilder<(DiagnosticAnalyzer, DocumentAnalysisData)> analyzersAndExistingData, + ArrayBuilder analyzersWithState, ImmutableArray oldMemberSpans, PooledDictionary> builder) { - if (analyzersAndExistingData.Count == 0) + if (analyzersWithState.Count == 0) return; executor = executor.With(analysisScope.WithSpan(null)); - await ExecuteAnalyzersAsync(executor, analyzersAndExistingData, oldMemberSpans, builder).ConfigureAwait(false); + await ExecuteAnalyzersAsync(executor, analyzersWithState, oldMemberSpans, builder).ConfigureAwait(false); } async Task ExecuteAnalyzersAsync( DocumentAnalysisExecutor executor, - IEnumerable<(DiagnosticAnalyzer, DocumentAnalysisData)> analyzersAndExistingData, + ArrayBuilder analyzersWithState, ImmutableArray oldMemberSpans, PooledDictionary> builder) { @@ -175,9 +176,9 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) Debug.Assert(changedMember != null); Debug.Assert(analysisScope.Kind == AnalysisKind.Semantic); - foreach (var (analyzer, existingData) in analyzersAndExistingData) + foreach (var analyzerWithState in analyzersWithState) { - var diagnostics = await computeAnalyzerDiagnosticsAsync(analyzer, executor, cancellationToken).ConfigureAwait(false); + var diagnostics = await computeAnalyzerDiagnosticsAsync(analyzerWithState.Analyzer, executor, cancellationToken).ConfigureAwait(false); // If we computed the diagnostics just for a span, then we are performing incremental analysis. // We need to compute the full document diagnostics by re-using diagnostics outside the changed @@ -187,12 +188,12 @@ public void UpdateDocumentWithCachedDiagnostics(Document document) Debug.Assert(analysisScope.Span.Value == changedMember.FullSpan); diagnostics = await GetUpdatedDiagnosticsForMemberEditAsync( - diagnostics, existingData, analyzer, + diagnostics, analyzerWithState.ExistingData, analyzerWithState.Analyzer, executor, changedMember, changedMemberId, oldMemberSpans, computeAnalyzerDiagnosticsAsync, cancellationToken).ConfigureAwait(false); } - builder.Add(analyzer, diagnostics); + builder.Add(analyzerWithState.Analyzer, diagnostics); } } } @@ -381,8 +382,13 @@ static DiagnosticData UpdateLocations(DiagnosticData diagnostic, SyntaxTree tree DiagnosticDataLocation UpdateLocation(DiagnosticDataLocation location) { var diagnosticSpan = location.UnmappedFileSpan.GetClampedTextSpan(text); - var start = Math.Min(Math.Max(diagnosticSpan.Start + delta, 0), tree.Length); - var newSpan = new TextSpan(start, start >= tree.Length ? 0 : diagnosticSpan.Length); + var start = Math.Max(diagnosticSpan.Start + delta, 0); + var end = start + diagnosticSpan.Length; + if (start >= tree.Length) + start = tree.Length - 1; + if (end >= tree.Length) + end = tree.Length - 1; + var newSpan = new TextSpan(start, end - start); return location.WithSpan(newSpan, tree); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs index 5d439d49fc467..8e2f1080ff50e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.ProjectStates.cs @@ -54,7 +54,7 @@ public IEnumerable GetAllProjectStateSets() { // check if the analyzer references have changed since the last time we updated the map: if (_projectAnalyzerStateMap.TryGetValue(project.Id, out var entry) && - entry.AnalyzerReferences.Equals(project.AnalyzerReferences)) + entry.AnalyzerReferences.SequenceEqual(project.AnalyzerReferences)) { return entry; } 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 6caf878aa7b2b..b45878bee3d3c 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -191,52 +191,6 @@ public ImmutableArray CreateBuildOnlyProjectStateSet(Project project) return stateSets.ToImmutable(); } - /// - /// Determines if any of the state sets in match a specified predicate. - /// - /// - /// This method avoids the performance overhead of calling for the - /// specific case where the result is only used for testing if any element meets certain conditions. - /// - public bool HasAnyHostStateSet(Func match, TArg arg) - { - foreach (var (_, hostStateSet) in _hostAnalyzerStateMap) - { - foreach (var stateSet in hostStateSet.OrderedStateSets) - { - if (match(stateSet, arg)) - return true; - } - } - - return false; - } - - /// - /// Determines if any of the state sets in for a specific project - /// match a specified predicate. - /// - /// - /// This method avoids the performance overhead of calling for the - /// specific case where the result is only used for testing if any element meets certain conditions. - /// - /// Note that host state sets (i.e. ones retured by are not tested - /// by this method. - /// - public bool HasAnyProjectStateSet(ProjectId projectId, Func match, TArg arg) - { - if (_projectAnalyzerStateMap.TryGetValue(projectId, out var entry)) - { - foreach (var (_, stateSet) in entry.StateSetMap) - { - if (match(stateSet, arg)) - return true; - } - } - - return false; - } - public bool OnProjectRemoved(IEnumerable stateSets, ProjectId projectId) { var removed = false; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index f565959af0d30..ed7aef2542cd7 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -37,20 +37,6 @@ public StateSet(string language, DiagnosticAnalyzer analyzer) _projectStates = new ConcurrentDictionary(concurrencyLevel: 2, capacity: 1); } - [PerformanceSensitive("https://github.com/dotnet/roslyn/issues/34761", AllowCaptures = false, AllowGenericEnumeration = false)] - public bool ContainsAnyDocumentOrProjectDiagnostics(ProjectId projectId) - { - foreach (var (documentId, state) in _activeFileStates) - { - if (documentId.ProjectId == projectId && !state.IsEmpty) - { - return true; - } - } - - return _projectStates.TryGetValue(projectId, out var projectState) && !projectState.IsEmpty(); - } - public IEnumerable GetProjectsWithDiagnostics() { // quick bail out @@ -129,12 +115,13 @@ public async Task OnDocumentOpenedAsync(TextDocument document) } var result = await projectState.GetAnalysisDataAsync(document, avoidLoadingData: false, CancellationToken.None).ConfigureAwait(false); + var text = await document.GetTextAsync(CancellationToken.None).ConfigureAwait(false); // store analysis result to active file state: var activeFileState = GetOrCreateActiveFileState(document.Id); - activeFileState.Save(AnalysisKind.Syntax, new DocumentAnalysisData(result.Version, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax))); - activeFileState.Save(AnalysisKind.Semantic, new DocumentAnalysisData(result.Version, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic))); + activeFileState.Save(AnalysisKind.Syntax, new DocumentAnalysisData(result.Version, text.Lines.Count, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Syntax))); + activeFileState.Save(AnalysisKind.Semantic, new DocumentAnalysisData(result.Version, text.Lines.Count, result.GetDocumentDiagnostics(document.Id, AnalysisKind.Semantic))); return true; } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 19927eabf98fb..6a10fee2ec89a 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -87,13 +87,6 @@ private void OnGlobalOptionChanged(object? sender, OptionChangedEventArgs e) internal IGlobalOptionService GlobalOptions => AnalyzerService.GlobalOptions; internal DiagnosticAnalyzerInfoCache DiagnosticAnalyzerInfoCache => _diagnosticAnalyzerRunner.AnalyzerInfoCache; - [PerformanceSensitive("https://github.com/dotnet/roslyn/issues/54400", Constraint = "Avoid calling GetAllHostStateSets on this hot path.")] - public bool ContainsDiagnostics(ProjectId projectId) - { - return _stateManager.HasAnyHostStateSet(static (stateSet, arg) => stateSet.ContainsAnyDocumentOrProjectDiagnostics(arg), projectId) - || _stateManager.HasAnyProjectStateSet(projectId, static (stateSet, arg) => stateSet.ContainsAnyDocumentOrProjectDiagnostics(arg), projectId); - } - private void OnProjectAnalyzerReferenceChanged(object? sender, ProjectAnalyzerReferenceChangedEventArgs e) { if (e.Removed.Length == 0) @@ -162,12 +155,12 @@ private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId p } private void RaiseDiagnosticsCreated( - Project project, StateSet stateSet, ImmutableArray items, Action raiseEvents) + Project project, DiagnosticAnalyzer analyzer, ImmutableArray items, Action raiseEvents) { Contract.ThrowIfFalse(project.Solution.Workspace == Workspace); raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(stateSet, project.Id, AnalysisKind.NonLocal), + CreateId(analyzer, project.Id, AnalysisKind.NonLocal), project.Solution.Workspace, project.Solution, project.Id, @@ -176,12 +169,12 @@ private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId p } private void RaiseDiagnosticsRemoved( - ProjectId projectId, Solution? solution, StateSet stateSet, Action raiseEvents) + ProjectId projectId, Solution? solution, DiagnosticAnalyzer analyzer, Action raiseEvents) { Contract.ThrowIfFalse(solution == null || solution.Workspace == Workspace); raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(stateSet, projectId, AnalysisKind.NonLocal), + CreateId(analyzer, projectId, AnalysisKind.NonLocal), Workspace, solution, projectId, @@ -189,12 +182,12 @@ private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId p } private void RaiseDiagnosticsCreated( - TextDocument document, StateSet stateSet, AnalysisKind kind, ImmutableArray items, Action raiseEvents) + TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray items, Action raiseEvents) { Contract.ThrowIfFalse(document.Project.Solution.Workspace == Workspace); raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsCreated( - CreateId(stateSet, document.Id, kind), + CreateId(analyzer, document.Id, kind), document.Project.Solution.Workspace, document.Project.Solution, document.Project.Id, @@ -203,23 +196,23 @@ private void ClearAllDiagnostics(ImmutableArray stateSets, ProjectId p } private void RaiseDiagnosticsRemoved( - DocumentId documentId, Solution? solution, StateSet stateSet, AnalysisKind kind, Action raiseEvents) + DocumentId documentId, Solution? solution, DiagnosticAnalyzer analyzer, AnalysisKind kind, Action raiseEvents) { Contract.ThrowIfFalse(solution == null || solution.Workspace == Workspace); raiseEvents(DiagnosticsUpdatedArgs.DiagnosticsRemoved( - CreateId(stateSet, documentId, kind), + CreateId(analyzer, documentId, kind), Workspace, solution, documentId.ProjectId, documentId)); } - private static object CreateId(StateSet stateSet, DocumentId documentId, AnalysisKind kind) - => new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, documentId, kind); + private static object CreateId(DiagnosticAnalyzer analyzer, DocumentId documentId, AnalysisKind kind) + => new LiveDiagnosticUpdateArgsId(analyzer, documentId, kind); - private static object CreateId(StateSet stateSet, ProjectId projectId, AnalysisKind kind) - => new LiveDiagnosticUpdateArgsId(stateSet.Analyzer, projectId, 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); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs index 52c22790fe0dd..3eb4a12583031 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_BuildSynchronization.cs @@ -78,7 +78,7 @@ internal partial class DiagnosticIncrementalAnalyzer // Enqueue re-analysis of active document with high-priority right away. if (_documentTrackingService.GetActiveDocument(solution) is { } activeDocument) { - AnalyzerService.Reanalyze(Workspace, documentIds: ImmutableArray.Create(activeDocument.Id), highPriority: true); + AnalyzerService.Reanalyze(Workspace, projectIds: null, documentIds: ImmutableArray.Create(activeDocument.Id), highPriority: true); } // Enqueue remaining re-analysis with normal priority on a separate task queue @@ -86,14 +86,14 @@ internal partial class DiagnosticIncrementalAnalyzer _ = postBuildAndErrorListRefreshTaskQueue.ScheduleTask(nameof(SynchronizeWithBuildAsync), () => { // Enqueue re-analysis of open documents. - AnalyzerService.Reanalyze(Workspace, documentIds: Workspace.GetOpenDocumentIds()); + 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)); + AnalyzerService.Reanalyze(Workspace, projectsByLanguage.Select(p => p.Id), documentIds: null, highPriority: false); } } }, cancellationToken); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 4ca7aad1e7abe..7df4f9c5eebbe 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer { - public Task> GetSpecificCachedDiagnosticsAsync(Solution solution, object id, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetSpecificCachedDiagnosticsAsync(Solution solution, object id, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) { if (id is not LiveDiagnosticUpdateArgsId argsId) { @@ -25,16 +25,16 @@ public Task> GetSpecificCachedDiagnosticsAsync(So return new IdeCachedDiagnosticGetter(this, solution, projectId, documentId, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetSpecificDiagnosticsAsync(argsId.Analyzer, argsId.Kind, cancellationToken); } - public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetCachedDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeCachedDiagnosticGetter(this, solution, projectId, documentId, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetDiagnosticsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds: null, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId, diagnosticIds, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetDiagnosticsAsync(cancellationToken); - public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics = false, bool includeNonLocalDocumentDiagnostics = true, CancellationToken cancellationToken = default) + public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds: diagnosticIds, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); private abstract class DiagnosticGetter diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 3c04d4469c10f..e81f86e1b666d 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -24,12 +24,12 @@ internal partial class DiagnosticIncrementalAnalyzer { public async Task TryAppendDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, ArrayBuilder result, Func? shouldIncludeDiagnostic, - bool includeSuppressedDiagnostics, bool includeCompilerDiagnostics, CodeActionRequestPriority priority, bool blockForData, + bool includeSuppressedDiagnostics, bool includeCompilerDiagnostics, ICodeActionRequestPriorityProvider priorityProvider, bool blockForData, Func? addOperationScope, DiagnosticKind diagnosticKinds, bool isExplicit, CancellationToken cancellationToken) { var getter = await LatestDiagnosticsForSpanGetter.CreateAsync( this, document, range, blockForData, addOperationScope, includeSuppressedDiagnostics, includeCompilerDiagnostics, - priority, shouldIncludeDiagnostic, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + priorityProvider, shouldIncludeDiagnostic, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); return await getter.TryGetAsync(result, cancellationToken).ConfigureAwait(false); } @@ -39,7 +39,7 @@ internal partial class DiagnosticIncrementalAnalyzer Func? shouldIncludeDiagnostic, bool includeSuppressedDiagnostics, bool includeCompilerDiagnostics, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, bool blockForData, Func? addOperationScope, DiagnosticKind diagnosticKinds, @@ -49,7 +49,7 @@ internal partial class DiagnosticIncrementalAnalyzer using var _ = ArrayBuilder.GetInstance(out var list); var result = await TryAppendDiagnosticsForSpanAsync( document, range, list, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics, - priority, blockForData, addOperationScope, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + priorityProvider, blockForData, addOperationScope, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); Debug.Assert(result); return list.ToImmutable(); } @@ -74,7 +74,7 @@ private sealed class LatestDiagnosticsForSpanGetter private readonly TextSpan? _range; private readonly bool _blockForData; private readonly bool _includeSuppressedDiagnostics; - private readonly CodeActionRequestPriority _priority; + private readonly ICodeActionRequestPriorityProvider _priorityProvider; private readonly Func? _shouldIncludeDiagnostic; private readonly bool _includeCompilerDiagnostics; private readonly Func? _addOperationScope; @@ -94,7 +94,7 @@ private sealed class LatestDiagnosticsForSpanGetter Func? addOperationScope, bool includeSuppressedDiagnostics, bool includeCompilerDiagnostics, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, Func? shouldIncludeDiagnostic, DiagnosticKind diagnosticKinds, bool isExplicit, @@ -112,11 +112,14 @@ private sealed class LatestDiagnosticsForSpanGetter // updating the error list simultaneously. var cacheFullDocumentDiagnostics = owner.AnalyzerService.GlobalOptions.IsLspPullDiagnostics(); + // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span. + // We clear out range for such cases as we are computing full document diagnostics. + if (range == new TextSpan(0, text.Length)) + range = null; + // We log performance info when we are computing diagnostics for a span // and also blocking for data, i.e. for lightbulb code path for "Ctrl + Dot" user command. - // Note that some callers, such as diagnostic tagger, might pass in a range equal to the entire document span, - // so we also check that the range length is lesser then the document text length. - var logPerformanceInfo = range.HasValue && blockForData && range.Value.Length < text.Length; + var logPerformanceInfo = range.HasValue && blockForData; var compilationWithAnalyzers = await GetOrCreateCompilationWithAnalyzersAsync(document.Project, ideOptions, stateSets, includeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); // If we are computing full document diagnostics, we will attempt to perform incremental @@ -128,7 +131,7 @@ private sealed class LatestDiagnosticsForSpanGetter return new LatestDiagnosticsForSpanGetter( owner, compilationWithAnalyzers, document, text, stateSets, shouldIncludeDiagnostic, includeCompilerDiagnostics, - range, blockForData, addOperationScope, includeSuppressedDiagnostics, priority, cacheFullDocumentDiagnostics, + range, blockForData, addOperationScope, includeSuppressedDiagnostics, priorityProvider, cacheFullDocumentDiagnostics, isExplicit, logPerformanceInfo, incrementalAnalysis, diagnosticKinds); } @@ -170,7 +173,7 @@ private sealed class LatestDiagnosticsForSpanGetter bool blockForData, Func? addOperationScope, bool includeSuppressedDiagnostics, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, bool cacheFullDocumentDiagnostics, bool isExplicit, bool logPerformanceInfo, @@ -188,7 +191,7 @@ private sealed class LatestDiagnosticsForSpanGetter _blockForData = blockForData; _addOperationScope = addOperationScope; _includeSuppressedDiagnostics = includeSuppressedDiagnostics; - _priority = priority; + _priorityProvider = priorityProvider; _cacheFullDocumentDiagnostics = cacheFullDocumentDiagnostics; _isExplicit = isExplicit; _logPerformanceInfo = logPerformanceInfo; @@ -203,20 +206,20 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat var containsFullResult = true; // Try to get cached diagnostics, and also compute non-cached state sets that need diagnostic computation. - using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); + using var _1 = ArrayBuilder.GetInstance(out var syntaxAnalyzers); // If we are performing incremental member edit analysis to compute diagnostics incrementally, // we divide the analyzers into those that support span-based incremental analysis and // those that do not support incremental analysis and must be executed for the entire document. // Otherwise, if we are not performing incremental analysis, all semantic analyzers are added // to the span-based analyzer set as we want to compute diagnostics only for the given span. - using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); - using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); + using var _2 = ArrayBuilder.GetInstance(out var semanticSpanBasedAnalyzers); + using var _3 = ArrayBuilder.GetInstance(out var semanticDocumentBasedAnalyzers); foreach (var stateSet in _stateSets) { var analyzer = stateSet.Analyzer; - if (!ShouldIncludeAnalyzer(analyzer, _shouldIncludeDiagnostic, _owner)) + if (!ShouldIncludeAnalyzer(analyzer, _shouldIncludeDiagnostic, _priorityProvider, _owner)) continue; bool includeSyntax = true, includeSemantic = true; @@ -231,22 +234,36 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat : _diagnosticKind == DiagnosticKind.AnalyzerSemantic; } - if (includeSyntax && !await TryAddCachedDocumentDiagnosticsAsync(stateSet, AnalysisKind.Syntax, list, cancellationToken).ConfigureAwait(false)) - syntaxAnalyzers.Add(stateSet); + includeSyntax = includeSyntax && analyzer.SupportAnalysisKind(AnalysisKind.Syntax); + includeSemantic = includeSemantic && analyzer.SupportAnalysisKind(AnalysisKind.Semantic) && _document is Document; - if (includeSemantic && - _document is Document && - !await TryAddCachedDocumentDiagnosticsAsync(stateSet, AnalysisKind.Semantic, list, cancellationToken).ConfigureAwait(false)) + if (includeSyntax || includeSemantic) { - if (ShouldRunSemanticAnalysis(stateSet.Analyzer, _incrementalAnalysis, _blockForData, - semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers, out var stateSets)) + var state = stateSet.GetOrCreateActiveFileState(_document.Id); + + if (includeSyntax) { - stateSets.Add(stateSet); + var existingData = state.GetAnalysisData(AnalysisKind.Syntax); + if (!await TryAddCachedDocumentDiagnosticsAsync(stateSet.Analyzer, AnalysisKind.Syntax, existingData, list, cancellationToken).ConfigureAwait(false)) + syntaxAnalyzers.Add(new AnalyzerWithState(stateSet.Analyzer, state, existingData)); } - else + + if (includeSemantic) { - Debug.Assert(!_blockForData); - containsFullResult = false; + var existingData = state.GetAnalysisData(AnalysisKind.Semantic); + if (!await TryAddCachedDocumentDiagnosticsAsync(stateSet.Analyzer, AnalysisKind.Semantic, existingData, list, cancellationToken).ConfigureAwait(false)) + { + if (ShouldRunSemanticAnalysis(stateSet.Analyzer, _incrementalAnalysis, _blockForData, + semanticSpanBasedAnalyzers, semanticDocumentBasedAnalyzers, out var stateSets)) + { + stateSets.Add(new AnalyzerWithState(stateSet.Analyzer, state, existingData)); + } + else + { + Debug.Assert(!_blockForData); + containsFullResult = false; + } + } } } } @@ -269,15 +286,25 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat static bool ShouldIncludeAnalyzer( DiagnosticAnalyzer analyzer, Func? shouldIncludeDiagnostic, + ICodeActionRequestPriorityProvider priorityProvider, DiagnosticIncrementalAnalyzer owner) { + // Skip executing analyzer if its priority does not match the request priority. + if (!priorityProvider.MatchesPriority(analyzer)) + return false; + // Special case DocumentDiagnosticAnalyzer to never skip these document analyzers // based on 'shouldIncludeDiagnostic' predicate. More specifically, TS has special document // analyzer which report 0 supported diagnostics, but we always want to execute it. if (analyzer is DocumentDiagnosticAnalyzer) - { return true; - } + + // Special case GeneratorDiagnosticsPlaceholderAnalyzer to never skip it based on + // 'shouldIncludeDiagnostic' predicate. More specifically, this is a placeholder analyzer + // for threading through all source generator reported diagnostics, but this special analyzer + // reports 0 supported diagnostics, and we always want to execute it. + if (analyzer is GeneratorDiagnosticsPlaceholderAnalyzer) + return true; // Skip analyzer if none of its reported diagnostics should be included. if (shouldIncludeDiagnostic != null && @@ -293,9 +320,9 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat DiagnosticAnalyzer analyzer, bool incrementalAnalysis, bool blockForData, - ArrayBuilder semanticSpanBasedAnalyzers, - ArrayBuilder semanticDocumentBasedAnalyzers, - [NotNullWhen(true)] out ArrayBuilder? selectedStateSets) + ArrayBuilder semanticSpanBasedAnalyzers, + ArrayBuilder semanticDocumentBasedAnalyzers, + [NotNullWhen(true)] out ArrayBuilder? selectedStateSets) { // If the caller doesn't want us to force compute diagnostics, // we don't run semantic analysis. @@ -328,26 +355,16 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat /// Returns if we were able to add the cached diagnostics and we do not need to compute them fresh. /// private async Task TryAddCachedDocumentDiagnosticsAsync( - StateSet stateSet, + DiagnosticAnalyzer analyzer, AnalysisKind kind, + DocumentAnalysisData existingData, ArrayBuilder list, CancellationToken cancellationToken) { - if (!stateSet.Analyzer.SupportAnalysisKind(kind) || - !MatchesPriority(stateSet.Analyzer)) - { - // In the case where the analyzer doesn't support the requested kind or priority, act as if we succeeded, but just - // added no items to the result. Effectively we did add the cached values, just that all the values that could have - // been added have been filtered out. We do not want to then compute the up to date values in the caller. - return true; - } - - // make sure we get state even when none of our analyzer has ran yet. - // but this shouldn't create analyzer that doesn't belong to this project (language) - var state = stateSet.GetOrCreateActiveFileState(_document.Id); + Debug.Assert(analyzer.SupportAnalysisKind(kind)); + Debug.Assert(_priorityProvider.MatchesPriority(analyzer)); // see whether we can use existing info - var existingData = state.GetAnalysisData(kind); var version = await GetDiagnosticVersionAsync(_document.Project, cancellationToken).ConfigureAwait(false); if (existingData.Version == version) { @@ -364,7 +381,7 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat } private async Task ComputeDocumentDiagnosticsAsync( - ImmutableArray stateSets, + ImmutableArray analyzersWithState, AnalysisKind kind, TextSpan? span, ArrayBuilder builder, @@ -372,14 +389,30 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat CancellationToken cancellationToken) { Debug.Assert(!incrementalAnalysis || kind == AnalysisKind.Semantic); - Debug.Assert(!incrementalAnalysis || stateSets.All(stateSet => stateSet.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); + Debug.Assert(!incrementalAnalysis || analyzersWithState.All(analyzerWithState => analyzerWithState.Analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis())); - stateSets = stateSets.WhereAsArray(s => MatchesPriority(s.Analyzer)); + using var _ = ArrayBuilder.GetInstance(analyzersWithState.Length, out var filteredAnalyzersWithStateBuilder); + foreach (var analyzerWithState in analyzersWithState) + { + Debug.Assert(_priorityProvider.MatchesPriority(analyzerWithState.Analyzer)); - if (stateSets.IsEmpty) + // Check if this is an expensive analyzer that needs to be de-prioritized to a lower priority bucket. + // If so, we skip this analyzer from execution in the current priority bucket. + // We will subsequently execute this analyzer in the lower priority bucket. + if (await TryDeprioritizeAnalyzerAsync(analyzerWithState.Analyzer, analyzerWithState.ExistingData).ConfigureAwait(false)) + { + continue; + } + + filteredAnalyzersWithStateBuilder.Add(analyzerWithState); + } + + if (filteredAnalyzersWithStateBuilder.Count == 0) return; - var analyzers = stateSets.SelectAsArray(stateSet => stateSet.Analyzer); + analyzersWithState = filteredAnalyzersWithStateBuilder.ToImmutable(); + + var analyzers = analyzersWithState.SelectAsArray(stateSet => stateSet.Analyzer); var analysisScope = new DocumentAnalysisScope(_document, span, analyzers, kind); var executor = new DocumentAnalysisExecutor(analysisScope, _compilationWithAnalyzers, _owner._diagnosticAnalyzerRunner, _isExplicit, _logPerformanceInfo); var version = await GetDiagnosticVersionAsync(_document.Project, cancellationToken).ConfigureAwait(false); @@ -389,7 +422,7 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat { diagnosticsMap = await _owner._incrementalMemberEditAnalyzer.ComputeDiagnosticsAsync( executor, - stateSets, + analyzersWithState, version, ComputeDocumentDiagnosticsForAnalyzerCoreAsync, ComputeDocumentDiagnosticsCoreAsync, @@ -400,22 +433,110 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat diagnosticsMap = await ComputeDocumentDiagnosticsCoreAsync(executor, cancellationToken).ConfigureAwait(false); } - foreach (var stateSet in stateSets) + foreach (var analyzerWithState in analyzersWithState) { - var diagnostics = diagnosticsMap[stateSet.Analyzer]; + var diagnostics = diagnosticsMap[analyzerWithState.Analyzer]; builder.AddRange(diagnostics.Where(ShouldInclude)); // Save the computed diagnostics if caching is enabled and diagnostics were computed for the entire document. if (_cacheFullDocumentDiagnostics && !span.HasValue) { - var state = stateSet.GetOrCreateActiveFileState(_document.Id); - var data = new DocumentAnalysisData(version, diagnostics); - state.Save(executor.AnalysisScope.Kind, data); + var data = new DocumentAnalysisData(version, _text.Lines.Count, diagnostics); + analyzerWithState.State.Save(executor.AnalysisScope.Kind, data); } } if (incrementalAnalysis) _owner._incrementalMemberEditAnalyzer.UpdateDocumentWithCachedDiagnostics((Document)_document); + + async Task TryDeprioritizeAnalyzerAsync(DiagnosticAnalyzer analyzer, DocumentAnalysisData existingData) + { + // PERF: In order to improve lightbulb performance, we perform de-prioritization optimization for certain analyzers + // that moves the analyzer to a lower priority bucket. However, to ensure that de-prioritization happens for very rare cases, + // we only perform this optimizations when following conditions are met: + // 1. We are performing semantic span-based analysis. + // 2. We are processing 'CodeActionRequestPriority.Normal' priority request. + // 3. Analyzer registers certain actions that are known to lead to high performance impact due to its broad analysis scope, + // such as SymbolStart/End actions and SemanticModel actions. + // 4. Analyzer did not report a diagnostic on the same line in prior document snapshot. + + // Conditions 1. and 2. + if (kind != AnalysisKind.Semantic || + !span.HasValue || + _priorityProvider.Priority != CodeActionRequestPriority.Normal) + { + return false; + } + + Debug.Assert(span.Value.Length < _text.Length); + + // Condition 3. + // Check if this is a candidate analyzer that can be de-prioritized into a lower priority bucket based on registered actions. + if (!await IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(analyzer).ConfigureAwait(false)) + { + return false; + } + + // Condition 4. + // We do not want to de-prioritize this analyzer if it reported a diagnostic on a prior document snapshot, + // such that diagnostic's start/end lines intersect the current analysis span's start/end lines. + // If an analyzer reported such a diagnostic, it is highly likely that the user intends to invoke the code fix + // for this diagnostic. Additionally, it is also highly likely that this analyzer will report a diagnostic + // on the current snapshot. So, we deem this as an important analyzer that should not be de-prioritized here. + // Note that we only perform this analysis if the prior document, whose existingData is cached, had same number + // of source lines as the current document snapshot. Otherwise, the start/end lines comparison across + // snapshots is not meaningful. + if (existingData.LineCount == _text.Lines.Count && + !existingData.Items.IsEmpty) + { + _text.GetLinesAndOffsets(span.Value, out var startLineNumber, out var _, out var endLineNumber, out var _); + + foreach (var diagnostic in existingData.Items) + { + if (diagnostic.DataLocation.UnmappedFileSpan.StartLinePosition.Line <= endLineNumber && + diagnostic.DataLocation.UnmappedFileSpan.EndLinePosition.Line >= startLineNumber) + { + return false; + } + } + } + + // 'LightbulbSkipExecutingDeprioritizedAnalyzers' option determines if we want to execute this analyzer + // in low priority bucket or skip it completely. If the option is not set, track the de-prioritized + // analyzer to be executed in low priority bucket. + // Note that 'AddDeprioritizedAnalyzerWithLowPriority' call below mutates the state in the provider to + // track this analyzer. This ensures that when the owner of this provider calls us back to execute + // the low priority bucket, we can still get back to this analyzer and execute it that time. + if (!_owner.GlobalOptions.GetOption(DiagnosticOptionsStorage.LightbulbSkipExecutingDeprioritizedAnalyzers)) + _priorityProvider.AddDeprioritizedAnalyzerWithLowPriority(analyzer); + + return true; + } + + // Returns true if this is an analyzer that is a candidate to be de-prioritized to + // 'CodeActionRequestPriority.Low' priority for improvement in analyzer + // execution performance for priority buckets above 'Low' priority. + // Based on performance measurements, currently only analyzers which register SymbolStart/End actions + // or SemanticModel actions are considered candidates to be de-prioritized. However, these semantics + // could be changed in future based on performance measurements. + async Task IsCandidateForDeprioritizationBasedOnRegisteredActionsAsync(DiagnosticAnalyzer analyzer) + { + // We deprioritize SymbolStart/End and SemanticModel analyzers from 'Normal' to 'Low' priority bucket, + // as these are computationally more expensive. + // Note that we never de-prioritize compiler analyzer, even though it registers a SemanticModel action. + if (_compilationWithAnalyzers == null || + analyzer.IsWorkspaceDiagnosticAnalyzer() || + analyzer.IsCompilerAnalyzer()) + { + return false; + } + + var telemetryInfo = await _compilationWithAnalyzers.GetAnalyzerTelemetryInfoAsync(analyzer, cancellationToken).ConfigureAwait(false); + if (telemetryInfo == null) + return false; + + return telemetryInfo.SymbolStartActionsCount > 0 || telemetryInfo.SemanticModelActionsCount > 0; + } } private async Task>> ComputeDocumentDiagnosticsCoreAsync( @@ -450,28 +571,6 @@ public async Task TryGetAsync(ArrayBuilder list, Cancellat } } - private bool MatchesPriority(DiagnosticAnalyzer analyzer) - { - // If caller isn't asking for prioritized result, then run all analyzers. - if (_priority == CodeActionRequestPriority.None) - return true; - - // 'CodeActionRequestPriority.Lowest' is used for suppression/configuration fixes, - // which requires all analyzer diagnostics. - if (_priority == CodeActionRequestPriority.Lowest) - return true; - - // The compiler analyzer always counts for any priority. It's diagnostics may be fixed - // by high pri or normal pri fixers. - if (analyzer.IsCompilerAnalyzer()) - return true; - - var analyzerPriority = analyzer is IBuiltInAnalyzer { RequestPriority: var requestPriority } - ? requestPriority - : CodeActionRequestPriority.Normal; - return _priority == analyzerPriority; - } - private bool ShouldInclude(DiagnosticData diagnostic) { return diagnostic.DocumentId == _document.Id && @@ -481,5 +580,7 @@ private bool ShouldInclude(DiagnosticData diagnostic) && (_shouldIncludeDiagnostic == null || _shouldIncludeDiagnostic(diagnostic.Id)); } } + + private sealed record class AnalyzerWithState(DiagnosticAnalyzer Analyzer, ActiveFileState State, DocumentAnalysisData ExistingData); } } 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 fc96b9538c1a0..737af28892ad7 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -66,33 +66,33 @@ private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKi // In near future, the diagnostic computation invocation into CompilationWithAnalyzers will be moved to OOP. // This should help simplify and/or remove the IDE layer diagnostic caching in devenv process. - // First attempt to fetch diagnostics from the cache, while computing the state sets for analyzers that are not cached. - using var _ = ArrayBuilder.GetInstance(out var nonCachedStateSets); + // First attempt to fetch diagnostics from the cache, while computing the analyzers that are not cached. + using var _ = ArrayBuilder<(DiagnosticAnalyzer analyzer, ActiveFileState state)>.GetInstance(out var nonCachedAnalyzersAndStates); foreach (var stateSet in stateSets) { - var data = TryGetCachedDocumentAnalysisData(document, stateSet, kind, version, + var (activeFileState, existingData) = TryGetCachedDocumentAnalysisData(document, stateSet, kind, version, backgroundAnalysisScope, compilerDiagnosticsScope, isActiveDocument, isVisibleDocument, isOpenDocument, isGeneratedRazorDocument, cancellationToken, out var isAnalyzerSuppressed); - if (data.HasValue) + if (existingData.HasValue) { - PersistAndRaiseDiagnosticsIfNeeded(data.Value, stateSet); + PersistAndRaiseDiagnosticsIfNeeded(existingData.Value, stateSet.Analyzer, activeFileState); } else if (!isAnalyzerSuppressed) { - nonCachedStateSets.Add(stateSet); + nonCachedAnalyzersAndStates.Add((stateSet.Analyzer, activeFileState)); } } // Then, compute the diagnostics for non-cached state sets, and cache and raise diagnostic reported events for these diagnostics. - if (nonCachedStateSets.Count > 0) + if (nonCachedAnalyzersAndStates.Count > 0) { - var analysisScope = new DocumentAnalysisScope(document, span: null, nonCachedStateSets.SelectAsArray(s => s.Analyzer), kind); + var analysisScope = new DocumentAnalysisScope(document, span: null, nonCachedAnalyzersAndStates.SelectAsArray(s => s.analyzer), kind); var executor = new DocumentAnalysisExecutor(analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, isExplicit: false, logPerformanceInfo: false, onAnalysisException: OnAnalysisException); var logTelemetry = GlobalOptions.GetOption(DiagnosticOptionsStorage.LogTelemetryForBackgroundAnalyzerExecution); - foreach (var stateSet in nonCachedStateSets) + foreach (var (analyzer, state) in nonCachedAnalyzersAndStates) { - var computedData = await ComputeDocumentAnalysisDataAsync(executor, stateSet, logTelemetry, cancellationToken).ConfigureAwait(false); - PersistAndRaiseDiagnosticsIfNeeded(computedData, stateSet); + var computedData = await ComputeDocumentAnalysisDataAsync(executor, analyzer, state, logTelemetry, cancellationToken).ConfigureAwait(false); + PersistAndRaiseDiagnosticsIfNeeded(computedData, analyzer, state); } } } @@ -101,19 +101,18 @@ private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKi throw ExceptionUtilities.Unreachable(); } - void PersistAndRaiseDiagnosticsIfNeeded(DocumentAnalysisData result, StateSet stateSet) + void PersistAndRaiseDiagnosticsIfNeeded(DocumentAnalysisData result, DiagnosticAnalyzer analyzer, ActiveFileState state) { if (result.FromCache == true) { - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.Items); + RaiseDocumentDiagnosticsIfNeeded(document, analyzer, kind, result.Items); return; } // no cancellation after this point. - var state = stateSet.GetOrCreateActiveFileState(document.Id); state.Save(kind, result.ToPersistData()); - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, result.OldItems, result.Items); + RaiseDocumentDiagnosticsIfNeeded(document, analyzer, kind, result.OldItems, result.Items); } void OnAnalysisException() @@ -325,9 +324,9 @@ private void RaiseDiagnosticsRemovedForDocument(DocumentId documentId, IEnumerab foreach (var stateSet in stateSets) { // clear all doucment diagnostics - RaiseDiagnosticsRemoved(documentId, solution: null, stateSet, AnalysisKind.Syntax, raiseEvents); - RaiseDiagnosticsRemoved(documentId, solution: null, stateSet, AnalysisKind.Semantic, raiseEvents); - RaiseDiagnosticsRemoved(documentId, solution: null, stateSet, AnalysisKind.NonLocal, raiseEvents); + RaiseDiagnosticsRemoved(documentId, solution: null, stateSet.Analyzer, AnalysisKind.Syntax, raiseEvents); + RaiseDiagnosticsRemoved(documentId, solution: null, stateSet.Analyzer, AnalysisKind.Semantic, raiseEvents); + RaiseDiagnosticsRemoved(documentId, solution: null, stateSet.Analyzer, AnalysisKind.NonLocal, raiseEvents); } }); } @@ -352,7 +351,7 @@ public Task RemoveProjectAsync(ProjectId projectId, CancellationToken cancellati foreach (var stateSet in stateSets) { // clear all project diagnostics - RaiseDiagnosticsRemoved(projectId, solution: null, stateSet, raiseEvents); + RaiseDiagnosticsRemoved(projectId, solution: null, stateSet.Analyzer, raiseEvents); } }); } @@ -508,17 +507,17 @@ private bool IsCandidateForFullSolutionAnalysis(DiagnosticAnalyzer analyzer, Pro }); } - private void RaiseDocumentDiagnosticsIfNeeded(TextDocument document, StateSet stateSet, AnalysisKind kind, ImmutableArray items) - => RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, ImmutableArray.Empty, items); + private void RaiseDocumentDiagnosticsIfNeeded(TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray items) + => RaiseDocumentDiagnosticsIfNeeded(document, analyzer, kind, ImmutableArray.Empty, items); private void RaiseDocumentDiagnosticsIfNeeded( - TextDocument document, StateSet stateSet, AnalysisKind kind, ImmutableArray oldItems, ImmutableArray newItems) + TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray oldItems, ImmutableArray newItems) { - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, AnalyzerService.RaiseDiagnosticsUpdated, forceUpdate: false); + RaiseDocumentDiagnosticsIfNeeded(document, analyzer, kind, oldItems, newItems, AnalyzerService.RaiseDiagnosticsUpdated, forceUpdate: false); } private void RaiseDocumentDiagnosticsIfNeeded( - TextDocument document, StateSet stateSet, AnalysisKind kind, + TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, DiagnosticAnalysisResult oldResult, DiagnosticAnalysisResult newResult, Action raiseEvents) { @@ -535,11 +534,11 @@ private void RaiseDocumentDiagnosticsIfNeeded(TextDocument document, StateSet st var oldItems = oldResult.GetDocumentDiagnostics(document.Id, kind); var newItems = newResult.GetDocumentDiagnostics(document.Id, kind); - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, kind, oldItems, newItems, raiseEvents, forceUpdate); + RaiseDocumentDiagnosticsIfNeeded(document, analyzer, kind, oldItems, newItems, raiseEvents, forceUpdate); } private void RaiseDocumentDiagnosticsIfNeeded( - TextDocument document, StateSet stateSet, AnalysisKind kind, + TextDocument document, DiagnosticAnalyzer analyzer, AnalysisKind kind, ImmutableArray oldItems, ImmutableArray newItems, Action raiseEvents, bool forceUpdate) @@ -550,7 +549,7 @@ private void RaiseDocumentDiagnosticsIfNeeded(TextDocument document, StateSet st return; } - RaiseDiagnosticsCreated(document, stateSet, kind, newItems, raiseEvents); + RaiseDiagnosticsCreated(document, analyzer, kind, newItems, raiseEvents); } private async Task RaiseProjectDiagnosticsCreatedAsync(Project project, StateSet stateSet, DiagnosticAnalysisResult oldAnalysisResult, DiagnosticAnalysisResult newAnalysisResult, Action raiseEvents, CancellationToken cancellationToken) @@ -581,7 +580,7 @@ private async Task RaiseProjectDiagnosticsCreatedAsync(Project project, StateSet continue; } - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, AnalysisKind.NonLocal, oldAnalysisResult, newAnalysisResult, raiseEvents); + RaiseDocumentDiagnosticsIfNeeded(document, stateSet.Analyzer, AnalysisKind.NonLocal, oldAnalysisResult, newAnalysisResult, raiseEvents); // we don't raise events for active file. it will be taken cared by active file analysis if (stateSet.IsActiveFile(documentId)) @@ -589,18 +588,18 @@ private async Task RaiseProjectDiagnosticsCreatedAsync(Project project, StateSet continue; } - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, AnalysisKind.Syntax, oldAnalysisResult, newAnalysisResult, raiseEvents); - RaiseDocumentDiagnosticsIfNeeded(document, stateSet, AnalysisKind.Semantic, oldAnalysisResult, newAnalysisResult, raiseEvents); + RaiseDocumentDiagnosticsIfNeeded(document, stateSet.Analyzer, AnalysisKind.Syntax, oldAnalysisResult, newAnalysisResult, raiseEvents); + RaiseDocumentDiagnosticsIfNeeded(document, stateSet.Analyzer, AnalysisKind.Semantic, oldAnalysisResult, newAnalysisResult, raiseEvents); } - RaiseDiagnosticsCreated(project, stateSet, newAnalysisResult.GetOtherDiagnostics(), raiseEvents); + RaiseDiagnosticsCreated(project, stateSet.Analyzer, newAnalysisResult.GetOtherDiagnostics(), raiseEvents); } private void RaiseProjectDiagnosticsRemoved(StateSet stateSet, ProjectId projectId, IEnumerable documentIds, bool handleActiveFile, Action raiseEvents) { foreach (var documentId in documentIds) { - RaiseDiagnosticsRemoved(documentId, solution: null, stateSet, AnalysisKind.NonLocal, raiseEvents); + RaiseDiagnosticsRemoved(documentId, solution: null, stateSet.Analyzer, AnalysisKind.NonLocal, raiseEvents); // we don't raise events for active file. it will be taken care of by active file analysis if (!handleActiveFile && stateSet.IsActiveFile(documentId)) @@ -608,11 +607,11 @@ private void RaiseProjectDiagnosticsRemoved(StateSet stateSet, ProjectId project continue; } - RaiseDiagnosticsRemoved(documentId, solution: null, stateSet, AnalysisKind.Syntax, raiseEvents); - RaiseDiagnosticsRemoved(documentId, solution: null, stateSet, AnalysisKind.Semantic, raiseEvents); + RaiseDiagnosticsRemoved(documentId, solution: null, stateSet.Analyzer, AnalysisKind.Syntax, raiseEvents); + RaiseDiagnosticsRemoved(documentId, solution: null, stateSet.Analyzer, AnalysisKind.Semantic, raiseEvents); } - RaiseDiagnosticsRemoved(projectId, solution: null, stateSet, raiseEvents); + RaiseDiagnosticsRemoved(projectId, solution: null, stateSet.Analyzer, raiseEvents); } } } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs index a28769f153e76..1c10675c682aa 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/DiagnosticOptionsStorage.cs @@ -13,5 +13,8 @@ internal sealed class DiagnosticOptionsStorage public static readonly Option2 LogTelemetryForBackgroundAnalyzerExecution = new( "dotnet_log_telemetry_for_background_analyzer_execution", defaultValue: false); + + public static readonly Option2 LightbulbSkipExecutingDeprioritizedAnalyzers = new( + "dotnet_lightbulb_skip_executing_deprioritized_analyzers", defaultValue: false); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs index 3729e2b21dffe..869de553c9fe4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/ExtractMethodOptionsStorage.cs @@ -18,7 +18,7 @@ internal static class ExtractMethodOptionsStorage public static ExtractMethodOptions GetExtractMethodOptions(this IGlobalOptionService globalOptions, string language) => new() { - DontPutOutOrRefOnStruct = globalOptions.GetOption(DontPutOutOrRefOnStruct, language) + DoNotPutOutOrRefOnStruct = globalOptions.GetOption(DoNotPutOutOrRefOnStruct, language) }; public static ExtractMethodGenerationOptions GetExtractMethodGenerationOptions(this IGlobalOptionService globalOptions, LanguageServices languageServices) @@ -33,6 +33,6 @@ public static ExtractMethodGenerationOptions GetExtractMethodGenerationOptions(t public static ValueTask GetExtractMethodGenerationOptionsAsync(this Document document, IGlobalOptionService globalOptions, CancellationToken cancellationToken) => document.GetExtractMethodGenerationOptionsAsync(globalOptions.GetExtractMethodGenerationOptions(document.Project.Services), cancellationToken); - public static readonly PerLanguageOption2 DontPutOutOrRefOnStruct = new( - "dotnet_extract_method_no_ref_or_out_structs", ExtractMethodOptions.Default.DontPutOutOrRefOnStruct); + public static readonly PerLanguageOption2 DoNotPutOutOrRefOnStruct = new( + "dotnet_extract_method_no_ref_or_out_structs", ExtractMethodOptions.Default.DoNotPutOutOrRefOnStruct); } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/InlineHintsOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/InlineHintsOptionsStorage.cs index a83e98ff503b1..fbb6760f3e92d 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/InlineHintsOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/InlineHintsOptionsStorage.cs @@ -31,78 +31,79 @@ public static InlineParameterHintsOptions GetInlineParameterHintsOptions(this IG }; public static InlineTypeHintsOptions GetInlineTypeHintsOptions(this IGlobalOptionService globalOptions, string language) - => new() - { - EnabledForTypes = globalOptions.GetOption(EnabledForTypes, language), - ForImplicitVariableTypes = globalOptions.GetOption(ForImplicitVariableTypes, language), - ForLambdaParameterTypes = globalOptions.GetOption(ForLambdaParameterTypes, language), - ForImplicitObjectCreation = globalOptions.GetOption(ForImplicitObjectCreation, language), - }; + => new() + { + EnabledForTypes = globalOptions.GetOption(EnabledForTypes, language), + ForImplicitVariableTypes = globalOptions.GetOption(ForImplicitVariableTypes, language), + ForLambdaParameterTypes = globalOptions.GetOption(ForLambdaParameterTypes, language), + ForImplicitObjectCreation = globalOptions.GetOption(ForImplicitObjectCreation, language), + }; - private static readonly OptionGroup s_inlineHintOptionGroup = new(name: "inline_hints", description: ""); + // Note: inlay hints is the term used in LSP, we Want to use the LSP name when communicate with the LSP client. + private static readonly OptionGroup s_inlayHintOptionGroup = new(name: "inlay_hints", description: ""); // Parameter hints public static readonly PerLanguageOption2 EnabledForParameters = - new("dotnet_enable_inline_hints_for_parameters", + new("dotnet_enable_inlay_hints_for_parameters", InlineParameterHintsOptions.Default.EnabledForParameters, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForLiteralParameters = - new("dotnet_enable_inline_hints_for_literal_parameters", + new("dotnet_enable_inlay_hints_for_literal_parameters", InlineParameterHintsOptions.Default.ForLiteralParameters, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForIndexerParameters = - new("dotnet_enable_inline_hints_for_indexer_parameters", + new("dotnet_enable_inlay_hints_for_indexer_parameters", InlineParameterHintsOptions.Default.ForIndexerParameters, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForObjectCreationParameters = - new("dotnet_enable_inline_hints_for_object_creation_parameters", + new("dotnet_enable_inlay_hints_for_object_creation_parameters", InlineParameterHintsOptions.Default.ForObjectCreationParameters, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForOtherParameters = - new("dotnet_enable_inline_hints_for_other_parameters", + new("dotnet_enable_inlay_hints_for_other_parameters", InlineParameterHintsOptions.Default.ForOtherParameters, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 SuppressForParametersThatDifferOnlyBySuffix = - new("dotnet_suppress_inline_hints_for_parameters_that_differ_only_by_suffix", + new("dotnet_suppress_inlay_hints_for_parameters_that_differ_only_by_suffix", InlineParameterHintsOptions.Default.SuppressForParametersThatDifferOnlyBySuffix, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 SuppressForParametersThatMatchMethodIntent = - new("dotnet_suppress_inline_hints_for_parameters_that_match_method_intent", + new("dotnet_suppress_inlay_hints_for_parameters_that_match_method_intent", InlineParameterHintsOptions.Default.SuppressForParametersThatMatchMethodIntent, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 SuppressForParametersThatMatchArgumentName = - new("dotnet_suppress_inline_hints_for_parameters_that_match_argument_name", + new("dotnet_suppress_inlay_hints_for_parameters_that_match_argument_name", InlineParameterHintsOptions.Default.SuppressForParametersThatMatchArgumentName, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); // Type Hints public static readonly PerLanguageOption2 EnabledForTypes = - new("csharp_enable_inline_hints_for_types", + new("csharp_enable_inlay_hints_for_types", defaultValue: InlineTypeHintsOptions.Default.EnabledForTypes, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForImplicitVariableTypes = - new("csharp_enable_inline_hints_for_implicit_variable_types", + new("csharp_enable_inlay_hints_for_implicit_variable_types", defaultValue: InlineTypeHintsOptions.Default.ForImplicitVariableTypes, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForLambdaParameterTypes = - new("csharp_enable_inline_hints_for_lambda_parameter_types", + new("csharp_enable_inlay_hints_for_lambda_parameter_types", defaultValue: InlineTypeHintsOptions.Default.ForLambdaParameterTypes, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); public static readonly PerLanguageOption2 ForImplicitObjectCreation = - new("csharp_enable_inline_hints_for_implicit_object_creation", + new("csharp_enable_inlay_hints_for_implicit_object_creation", defaultValue: InlineTypeHintsOptions.Default.ForImplicitObjectCreation, - group: s_inlineHintOptionGroup); + group: s_inlayHintOptionGroup); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationOptionsStorage.cs index e1a6bab38c206..81fa6b846b82e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationOptionsStorage.cs @@ -15,7 +15,9 @@ public static WorkspaceConfigurationOptions GetWorkspaceConfigurationOptions(thi CacheStorage: globalOptions.GetOption(CloudCacheFeatureFlag) ? StorageDatabase.CloudCache : globalOptions.GetOption(Database), EnableOpeningSourceGeneratedFiles: globalOptions.GetOption(EnableOpeningSourceGeneratedFilesInWorkspace) ?? globalOptions.GetOption(EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag), - DisableSharedSyntaxTrees: globalOptions.GetOption(DisableSharedSyntaxTrees)); + DisableSharedSyntaxTrees: globalOptions.GetOption(DisableSharedSyntaxTrees), + DeferCreatingRecoverableText: globalOptions.GetOption(DeferCreatingRecoverableText), + DisableRecoverableText: globalOptions.GetOption(DisableRecoverableText)); public static readonly Option2 Database = new( "dotnet_storage_database", WorkspaceConfigurationOptions.Default.CacheStorage, serializer: EditorConfigValueSerializer.CreateSerializerForEnum()); @@ -26,6 +28,12 @@ public static WorkspaceConfigurationOptions GetWorkspaceConfigurationOptions(thi public static readonly Option2 DisableSharedSyntaxTrees = new( "dotnet_disable_shared_syntax_trees", WorkspaceConfigurationOptions.Default.DisableSharedSyntaxTrees); + public static readonly Option2 DeferCreatingRecoverableText = new( + "dotnet_defer_creating_recoverable_text", WorkspaceConfigurationOptions.Default.DeferCreatingRecoverableText); + + public static readonly Option2 DisableRecoverableText = new( + "dotnet_disable_recoverable_text", WorkspaceConfigurationOptions.Default.DisableRecoverableText); + /// /// This option allows the user to enable this. We are putting this behind a feature flag for now since we could have extensions /// surprised by this and we want some time to work through those issues. diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index 2bdbd846be789..14c1f2367d754 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -36,9 +36,8 @@ internal class UnifiedSuggestedActionsSource ICodeFixService codeFixService, TextDocument document, TextSpan selection, - CodeActionRequestPriority priority, + ICodeActionRequestPriorityProvider priorityProvider, CodeActionOptionsProvider fallbackOptions, - bool isBlocking, Func addOperationScope, CancellationToken cancellationToken) { @@ -49,9 +48,8 @@ internal class UnifiedSuggestedActionsSource var fixes = await Task.Run(() => codeFixService.GetFixesAsync( document, selection, - priority, + priorityProvider, fallbackOptions, - isBlocking, addOperationScope, cancellationToken), cancellationToken).ConfigureAwait(false); @@ -440,7 +438,6 @@ private static bool IsBulkConfigurationAction(CodeAction action) TextSpan selection, CodeActionRequestPriority priority, CodeActionOptionsProvider options, - bool isBlocking, Func addOperationScope, bool filterOutsideSelection, CancellationToken cancellationToken) @@ -451,7 +448,7 @@ private static bool IsBulkConfigurationAction(CodeAction action) // the UI thread. var refactorings = await Task.Run( () => codeRefactoringService.GetRefactoringsAsync( - document, selection, priority, options, isBlocking, addOperationScope, + document, selection, priority, options, addOperationScope, cancellationToken), cancellationToken).ConfigureAwait(false); var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 45eb1ef5321bc..21021089de69a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnifiedSuggestions; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -24,56 +23,133 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions internal static class CodeActionHelpers { /// - /// Get, order, and filter code actions, and then transform them into VSCodeActions. + /// Get, order, and filter code actions, and then transform them into VSCodeActions or CodeActions based on . /// /// /// Used by CodeActionsHandler. /// - public static async Task GetVSCodeActionsAsync( + public static async Task GetVSCodeActionsAsync( CodeActionParams request, Document document, CodeActionOptionsProvider fallbackOptions, ICodeFixService codeFixService, ICodeRefactoringService codeRefactoringService, + bool hasVsLspCapability, CancellationToken cancellationToken) { var actionSets = await GetActionSetsAsync( document, fallbackOptions, codeFixService, codeRefactoringService, request.Range, cancellationToken).ConfigureAwait(false); if (actionSets.IsDefaultOrEmpty) - return Array.Empty(); + return Array.Empty(); - var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + using var _ = ArrayBuilder.GetInstance(out var codeActions); + // VS-LSP support nested code action, but standard LSP doesn't. + if (hasVsLspCapability) + { + var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - // Each suggested action set should have a unique set number, which is used for grouping code actions together. - var currentHighestSetNumber = 0; + // Each suggested action set should have a unique set number, which is used for grouping code actions together. + var currentHighestSetNumber = 0; - using var _ = ArrayBuilder.GetInstance(out var codeActions); - foreach (var set in actionSets) + foreach (var set in actionSets) + { + var currentSetNumber = ++currentHighestSetNumber; + foreach (var suggestedAction in set.Actions) + { + if (!IsCodeActionNotSupportedByLSP(suggestedAction)) + { + codeActions.Add(GenerateVSCodeAction( + request, documentText, + suggestedAction: suggestedAction, + codeActionKind: GetCodeActionKindFromSuggestedActionCategoryName(set.CategoryName!), + setPriority: set.Priority, + applicableRange: set.ApplicableToSpan.HasValue ? ProtocolConversions.TextSpanToRange(set.ApplicableToSpan.Value, documentText) : null, + currentSetNumber: currentSetNumber, + currentHighestSetNumber: ref currentHighestSetNumber)); + } + } + } + } + else { - var currentSetNumber = ++currentHighestSetNumber; - foreach (var suggestedAction in set.Actions) + foreach (var set in actionSets) { - // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options. - if (suggestedAction.OriginalCodeAction is CodeActionWithOptions) - continue; + foreach (var suggestedAction in set.Actions) + { + if (!IsCodeActionNotSupportedByLSP(suggestedAction)) + { + codeActions.AddRange(GenerateCodeActions( + request, + suggestedAction, + GetCodeActionKindFromSuggestedActionCategoryName(set.CategoryName!))); + } + } + } + } - // Skip code actions that requires non-document changes. We can't apply them in LSP currently. - // https://github.com/dotnet/roslyn/issues/48698 - if (suggestedAction.OriginalCodeAction.Tags.Contains(CodeAction.RequiresNonDocumentChange)) - continue; + return codeActions.ToArray(); + } + + private static bool IsCodeActionNotSupportedByLSP(IUnifiedSuggestedAction suggestedAction) + // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options. + => suggestedAction.OriginalCodeAction is CodeActionWithOptions + // Skip code actions that requires non-document changes. We can't apply them in LSP currently. + // https://github.com/dotnet/roslyn/issues/48698 + || suggestedAction.OriginalCodeAction.Tags.Contains(CodeAction.RequiresNonDocumentChange); + + /// + /// Generate the matching code actions for . If it contains nested code actions, flatten them into an array. + /// + private static LSP.CodeAction[] GenerateCodeActions( + CodeActionParams request, + IUnifiedSuggestedAction suggestedAction, + LSP.CodeActionKind codeActionKind, + string currentTitle = "") + { + if (!string.IsNullOrEmpty(currentTitle)) + { + // Adding a delimiter for nested code actions, e.g. 'Suppress or Configure issues|Suppress IDEXXXX|in Source' + currentTitle += '|'; + } - codeActions.Add(GenerateVSCodeAction( - request, documentText, - suggestedAction: suggestedAction, - codeActionKind: GetCodeActionKindFromSuggestedActionCategoryName(set.CategoryName!), - setPriority: set.Priority, - applicableRange: set.ApplicableToSpan.HasValue ? ProtocolConversions.TextSpanToRange(set.ApplicableToSpan.Value, documentText) : null, - currentSetNumber: currentSetNumber, - currentHighestSetNumber: ref currentHighestSetNumber)); + var codeAction = suggestedAction.OriginalCodeAction; + currentTitle += codeAction.Title; + + var diagnosticsForFix = GetApplicableDiagnostics(request.Context, suggestedAction); + + using var _ = ArrayBuilder.GetInstance(out var builder); + if (suggestedAction is UnifiedSuggestedActionWithNestedActions unifiedSuggestedActions) + { + foreach (var actionSet in unifiedSuggestedActions.NestedActionSets) + { + foreach (var action in actionSet.Actions) + { + // Filter the configure and suppress fixer if it is not VS LSP, because it would generate many nested code actions. + // Tracking issue: https://github.com/microsoft/language-server-protocol/issues/994 + if (action.OriginalCodeAction is not AbstractConfigurationActionWithNestedActions) + { + builder.AddRange(GenerateCodeActions( + request, + action, + codeActionKind, + currentTitle)); + } + } } } + else + { + builder.Add(new LSP.CodeAction + { + // Change this to -> because it is shown to the user + Title = currentTitle.Replace("|", " -> "), + Kind = codeActionKind, + Diagnostics = diagnosticsForFix, + Data = new CodeActionResolveData(currentTitle, codeAction.CustomTags, request.Range, request.TextDocument) + }); + } - return codeActions.ToArray(); + return builder.ToArray(); } private static VSInternalCodeAction GenerateVSCodeAction( @@ -143,29 +219,29 @@ internal static class CodeActionHelpers return nestedActions.ToArray(); } + } - static LSP.Diagnostic[]? GetApplicableDiagnostics(LSP.CodeActionContext context, IUnifiedSuggestedAction action) + private static LSP.Diagnostic[]? GetApplicableDiagnostics(CodeActionContext context, IUnifiedSuggestedAction action) + { + if (action is UnifiedCodeFixSuggestedAction codeFixAction && context.Diagnostics != null) { - if (action is UnifiedCodeFixSuggestedAction codeFixAction && context.Diagnostics != null) + // Associate the diagnostics from the request that match the diagnostic fixed by the code action by ID. + // The request diagnostics are already restricted to the code fix location by the request. + var diagnosticCodesFixedByAction = codeFixAction.CodeFix.Diagnostics.Select(d => d.Id); + using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); + foreach (var requestDiagnostic in context.Diagnostics) { - // Associate the diagnostics from the request that match the diagnostic fixed by the code action by ID. - // The request diagnostics are already restricted to the code fix location by the request. - var diagnosticCodesFixedByAction = codeFixAction.CodeFix.Diagnostics.Select(d => d.Id); - using var _ = ArrayBuilder.GetInstance(out var diagnosticsBuilder); - foreach (var requestDiagnostic in context.Diagnostics) + var diagnosticCode = requestDiagnostic.Code?.Value?.ToString(); + if (diagnosticCodesFixedByAction.Contains(diagnosticCode)) { - var diagnosticCode = requestDiagnostic.Code?.Value?.ToString(); - if (diagnosticCodesFixedByAction.Contains(diagnosticCode)) - { - diagnosticsBuilder.Add(requestDiagnostic); - } + diagnosticsBuilder.Add(requestDiagnostic); } - - return diagnosticsBuilder.ToArray(); } - return null; + return diagnosticsBuilder.ToArray(); } + + return null; } /// @@ -242,11 +318,11 @@ private static CodeAction GetNestedActionsFromActionSet(IUnifiedSuggestedAction var codeFixes = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync( document.Project.Solution.Workspace, codeFixService, document, textSpan, - CodeActionRequestPriority.None, - fallbackOptions, isBlocking: false, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false); + new DefaultCodeActionRequestPriorityProvider(), + fallbackOptions, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false); var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( - document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, CodeActionRequestPriority.None, fallbackOptions, isBlocking: false, + document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, CodeActionRequestPriority.None, fallbackOptions, addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken).ConfigureAwait(false); var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets( diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs index a414e7623e1a0..4e26e6175c29b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -90,7 +91,8 @@ public async Task HandleRequestAsync(LSP.CodeAction codeAction, var textDiffService = solution.Services.GetService(); - using var _ = ArrayBuilder.GetInstance(out var textDocumentEdits); + using var _1 = ArrayBuilder>.GetInstance(out var textDocumentEdits); + using var _2 = PooledHashSet.GetInstance(out var modifiedDocumentIds); foreach (var option in operations) { @@ -107,15 +109,72 @@ public async Task HandleRequestAsync(LSP.CodeAction codeAction, } var changes = applyChangesOperation.ChangedSolution.GetChanges(solution); + var newSolution = await applyChangesOperation.ChangedSolution.WithMergedLinkedFileChangesAsync(solution, changes, cancellationToken: cancellationToken).ConfigureAwait(false); + changes = newSolution.GetChanges(solution); + var projectChanges = changes.GetProjectChanges(); - // Ignore any non-document changes for now. Note though that LSP does support additional functionality - // (like create/rename/delete file). Once VS updates their LSP client impl to support this, we should - // add that support here. + // Don't apply changes in the presence of any non-document changes for now. Note though that LSP does + // support additional functionality (like create/rename/delete file). Once VS updates their LSP client + // impl to support this, we should add that support here. // // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit // // Tracked with: https://github.com/dotnet/roslyn/issues/65303 + foreach (var projectChange in projectChanges) + { + if (projectChange.GetAddedProjectReferences().Any() + || projectChange.GetRemovedProjectReferences().Any() + || projectChange.GetAddedMetadataReferences().Any() + || projectChange.GetRemovedMetadataReferences().Any() + || projectChange.GetAddedAnalyzerReferences().Any() + || projectChange.GetRemovedAnalyzerReferences().Any()) + { + // Changes to references are not currently supported + codeAction.Edit = new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; + return codeAction; + } + + if (projectChange.GetRemovedDocuments().Any() + || projectChange.GetRemovedAdditionalDocuments().Any() + || projectChange.GetRemovedAnalyzerConfigDocuments().Any()) + { + if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } + || !resourceOperations.Contains(ResourceOperationKind.Delete)) + { + // Removing documents is not supported by this workspace + codeAction.Edit = new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; + return codeAction; + } + } + + if (projectChange.GetAddedDocuments().Any() + || projectChange.GetAddedAdditionalDocuments().Any() + || projectChange.GetAddedAnalyzerConfigDocuments().Any()) + { + if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } + || !resourceOperations.Contains(ResourceOperationKind.Create)) + { + // Adding documents is not supported by this workspace + codeAction.Edit = new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; + return codeAction; + } + } + + if (projectChange.GetChangedDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution)) + || projectChange.GetChangedAdditionalDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution) + || projectChange.GetChangedAnalyzerConfigDocuments().Any(docId => HasDocumentNameChange(docId, newSolution, solution)))) + { + if (context.GetRequiredClientCapabilities() is not { Workspace.WorkspaceEdit.ResourceOperations: { } resourceOperations } + || !resourceOperations.Contains(ResourceOperationKind.Rename)) + { + // Rename documents is not supported by this workspace + codeAction.Edit = new LSP.WorkspaceEdit { DocumentChanges = Array.Empty() }; + return codeAction; + } + } + } + #if false // TO-DO: If the change involves adding or removing a document, execute via command instead of WorkspaceEdit @@ -144,22 +203,52 @@ public async Task HandleRequestAsync(LSP.CodeAction codeAction, #endif + // Removed documents + await AddTextDocumentDeletionsAsync( + projectChanges.SelectMany(pc => pc.GetRemovedDocuments()), + solution.GetDocument).ConfigureAwait(false); + + // Removed analyzer config documents + await AddTextDocumentDeletionsAsync( + projectChanges.SelectMany(pc => pc.GetRemovedAnalyzerConfigDocuments()), + solution.GetAnalyzerConfigDocument).ConfigureAwait(false); + + // Removed additional documents + await AddTextDocumentDeletionsAsync( + projectChanges.SelectMany(pc => pc.GetRemovedAdditionalDocuments()), + solution.GetAdditionalDocument).ConfigureAwait(false); + + // Added documents + await AddTextDocumentAdditionsAsync( + projectChanges.SelectMany(pc => pc.GetAddedDocuments()), + newSolution.GetDocument).ConfigureAwait(false); + + // Added analyzer config documents + await AddTextDocumentAdditionsAsync( + projectChanges.SelectMany(pc => pc.GetAddedAnalyzerConfigDocuments()), + newSolution.GetAnalyzerConfigDocument).ConfigureAwait(false); + + // Added additional documents + await AddTextDocumentAdditionsAsync( + projectChanges.SelectMany(pc => pc.GetAddedAdditionalDocuments()), + newSolution.GetAdditionalDocument).ConfigureAwait(false); + // Changed documents await AddTextDocumentEditsAsync( projectChanges.SelectMany(pc => pc.GetChangedDocuments()), - applyChangesOperation.ChangedSolution.GetDocument, + newSolution.GetDocument, solution.GetDocument).ConfigureAwait(false); // Changed analyzer config documents await AddTextDocumentEditsAsync( projectChanges.SelectMany(pc => pc.GetChangedAnalyzerConfigDocuments()), - applyChangesOperation.ChangedSolution.GetAnalyzerConfigDocument, + newSolution.GetAnalyzerConfigDocument, solution.GetAnalyzerConfigDocument).ConfigureAwait(false); // Changed additional documents await AddTextDocumentEditsAsync( projectChanges.SelectMany(pc => pc.GetChangedAdditionalDocuments()), - applyChangesOperation.ChangedSolution.GetAdditionalDocument, + newSolution.GetAdditionalDocument, solution.GetAdditionalDocument).ConfigureAwait(false); } @@ -167,6 +256,44 @@ public async Task HandleRequestAsync(LSP.CodeAction codeAction, return codeAction; + Task AddTextDocumentDeletionsAsync( + IEnumerable removedDocuments, + Func getOldDocument) + where TTextDocument : TextDocument + { + foreach (var docId in removedDocuments) + { + var oldTextDoc = getOldDocument(docId); + Contract.ThrowIfNull(oldTextDoc); + + textDocumentEdits.Add(new DeleteFile { Uri = oldTextDoc.GetURI() }); + } + + return Task.CompletedTask; + } + + async Task AddTextDocumentAdditionsAsync( + IEnumerable addedDocuments, + Func getNewDocument) + where TTextDocument : TextDocument + { + foreach (var docId in addedDocuments) + { + var newTextDoc = getNewDocument(docId); + Contract.ThrowIfNull(newTextDoc); + + // Create the document as empty + textDocumentEdits.Add(new CreateFile { Uri = newTextDoc.GetURI() }); + + // And then give it content + var newText = await newTextDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); + var emptyDocumentRange = new LSP.Range { Start = new Position { Line = 0, Character = 0 }, End = new Position { Line = 0, Character = 0 } }; + var edit = new TextEdit { Range = emptyDocumentRange, NewText = newText.ToString() }; + var documentIdentifier = new OptionalVersionedTextDocumentIdentifier { Uri = newTextDoc.GetURI() }; + textDocumentEdits.Add(new TextDocumentEdit { TextDocument = documentIdentifier, Edits = new[] { edit } }); + } + } + async Task AddTextDocumentEditsAsync( IEnumerable changedDocuments, Func getNewDocument, @@ -181,28 +308,56 @@ public async Task HandleRequestAsync(LSP.CodeAction codeAction, Contract.ThrowIfNull(oldTextDoc); Contract.ThrowIfNull(newTextDoc); - var oldText = await oldTextDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); + // For linked documents, only generated the document edit once. + if (modifiedDocumentIds.Add(docId)) + { + var oldText = await oldTextDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); - IEnumerable textChanges; + IEnumerable textChanges; - // Normal documents have a unique service for calculating minimal text edits. If we used the standard 'GetTextChanges' - // method instead, we would get a change that spans the entire document, which we ideally want to avoid. - if (newTextDoc is Document newDoc && oldTextDoc is Document oldDoc) - { - Contract.ThrowIfNull(textDiffService); - textChanges = await textDiffService.GetTextChangesAsync(oldDoc, newDoc, cancellationToken).ConfigureAwait(false); - } - else - { - var newText = await newTextDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); - textChanges = newText.GetTextChanges(oldText); - } + // Normal documents have a unique service for calculating minimal text edits. If we used the standard 'GetTextChanges' + // method instead, we would get a change that spans the entire document, which we ideally want to avoid. + if (newTextDoc is Document newDoc && oldTextDoc is Document oldDoc) + { + Contract.ThrowIfNull(textDiffService); + textChanges = await textDiffService.GetTextChangesAsync(oldDoc, newDoc, cancellationToken).ConfigureAwait(false); + } + else + { + var newText = await newTextDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); + textChanges = newText.GetTextChanges(oldText); + } - var edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray(); - var documentIdentifier = new OptionalVersionedTextDocumentIdentifier { Uri = newTextDoc.GetURI() }; - textDocumentEdits.Add(new TextDocumentEdit { TextDocument = documentIdentifier, Edits = edits }); + var edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray(); + + if (edits.Length > 0) + { + var documentIdentifier = new OptionalVersionedTextDocumentIdentifier { Uri = newTextDoc.GetURI() }; + textDocumentEdits.Add(new TextDocumentEdit { TextDocument = documentIdentifier, Edits = edits }); + } + + // Add Rename edit. + // Note: + // Client is expected to do the change in the order in which they are provided. + // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit + // So we would like to first edit the old document, then rename it. + if (oldTextDoc.Name != newTextDoc.Name) + { + textDocumentEdits.Add(new RenameFile() { OldUri = oldTextDoc.GetURI(), NewUri = newTextDoc.GetUriForRenamedDocument() }); + } + + var linkedDocuments = solution.GetRelatedDocumentIds(docId); + modifiedDocumentIds.AddRange(linkedDocuments); + } } } } + + private static bool HasDocumentNameChange(DocumentId documentId, Solution newSolution, Solution oldSolution) + { + var newDocument = newSolution.GetRequiredTextDocument(documentId); + var oldDocument = oldSolution.GetRequiredTextDocument(documentId); + return newDocument.Name != oldDocument.Name; + } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs index ab7eb0ea01bad..3a56b88cfe30c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs @@ -56,10 +56,10 @@ public async Task HandleRequestAsync(LSP.CodeActionParams requ Contract.ThrowIfNull(document); var options = _globalOptions.GetCodeActionOptionsProvider(); + var clientCapability = context.GetRequiredClientCapabilities(); var codeActions = await CodeActionHelpers.GetVSCodeActionsAsync( - request, document, options, _codeFixService, _codeRefactoringService, cancellationToken).ConfigureAwait(false); - + request, document, options, _codeFixService, _codeRefactoringService, hasVsLspCapability: clientCapability.HasVisualStudioLspCapability(), cancellationToken).ConfigureAwait(false); return codeActions; } } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensCache.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensCache.cs index 3b57c01808d99..5ae1c03dad1c5 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensCache.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensCache.cs @@ -27,7 +27,6 @@ public CodeLensCache() : base(maxCacheSize: 3) /// Cached data need to resolve a specific code lens item /// /// the list of nodes and locations for codelens members - /// the lsp document they came from /// the syntax version the codelenses were calculated against (to validate the resolve request) - internal record CodeLensCacheEntry(ImmutableArray CodeLensMembers, TextDocumentIdentifier TextDocumentIdentifier, VersionStamp SyntaxVersion); + internal record CodeLensCacheEntry(ImmutableArray CodeLensMembers, VersionStamp SyntaxVersion); } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs index 4baeffcfe88b8..5afc072fbb3ed 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs @@ -49,7 +49,7 @@ public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeLensParams r // Store the members in the resolve cache so that when we get a resolve request for a particular // member we can re-use the syntax node and span we already computed here. - var resultId = codeLensCache.UpdateCache(new CodeLensCache.CodeLensCacheEntry(members, request.TextDocument, syntaxVersion)); + var resultId = codeLensCache.UpdateCache(new CodeLensCache.CodeLensCacheEntry(members, syntaxVersion)); // TODO - Code lenses need to be refreshed by the server when we detect solution/project wide changes. // See https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1730462 @@ -63,7 +63,7 @@ public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeLensParams r { Range = range, Command = null, - Data = new CodeLensResolveData(resultId, i) + Data = new CodeLensResolveData(resultId, i, request.TextDocument) }; codeLenses.Add(codeLens); diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveData.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveData.cs index b9f9d6fd96dc6..fe5277d58c335 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveData.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveData.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.VisualStudio.LanguageServer.Protocol; + namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; /// @@ -9,4 +11,5 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; /// /// the resultId associated with the code lens list created on original request. /// the index of the specific code lens item in the original list. -internal sealed record CodeLensResolveData(long ResultId, int ListIndex); +/// the text document associated with the code lens to resolve. +internal sealed record CodeLensResolveData(long ResultId, int ListIndex, TextDocumentIdentifier TextDocument); diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index b6346e3b7fb39..f9e3e410005e5 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -33,12 +33,13 @@ public CodeLensResolveHandler(CodeLensCache codeLensCache) public bool RequiresLSPSolution => true; public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeLens request) - => GetCacheEntry(request).CacheEntry.TextDocumentIdentifier; + => GetCodeLensResolveData(request).TextDocument; public async Task HandleRequestAsync(LSP.CodeLens request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); - var (cacheEntry, memberToResolve) = GetCacheEntry(request); + var resolveData = GetCodeLensResolveData(request); + var (cacheEntry, memberToResolve) = GetCacheEntry(resolveData); var currentSyntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); var cachedSyntaxVersion = cacheEntry.SyntaxVersion; @@ -61,7 +62,7 @@ public async Task HandleRequestAsync(LSP.CodeLens request, Request CommandIdentifier = ClientReferencesCommand, Arguments = new object[] { - cacheEntry.TextDocumentIdentifier.Uri, + resolveData.TextDocument.Uri, request.Range.Start } }; @@ -71,14 +72,18 @@ public async Task HandleRequestAsync(LSP.CodeLens request, Request return request; } - private (CodeLensCache.CodeLensCacheEntry CacheEntry, CodeLensMember MemberToResolve) GetCacheEntry(LSP.CodeLens request) + private (CodeLensCache.CodeLensCacheEntry CacheEntry, CodeLensMember MemberToResolve) GetCacheEntry(CodeLensResolveData resolveData) { - var resolveData = (request.Data as JToken)?.ToObject(); - Contract.ThrowIfNull(resolveData, "Missing data for code lens resolve request"); - var cacheEntry = _codeLensCache.GetCachedEntry(resolveData.ResultId); Contract.ThrowIfNull(cacheEntry, "Missing cache entry for code lens resolve request"); return (cacheEntry, cacheEntry.CodeLensMembers[resolveData.ListIndex]); } + + private static CodeLensResolveData GetCodeLensResolveData(LSP.CodeLens codeLens) + { + var resolveData = (codeLens.Data as JToken)?.ToObject(); + Contract.ThrowIfNull(resolveData, "Missing data for code lens resolve request"); + return resolveData; + } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs index 2c3572b2fd63e..3550212dfafc5 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,16 +24,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { /// /// Handle a completion request. - /// - /// TODO - This must be moved to the MS.CA.LanguageServer.Protocol project once the - /// references to VS Icon types are removed. - /// See https://github.com/dotnet/roslyn/issues/55142 /// [ExportCSharpVisualBasicStatelessLspService(typeof(CompletionHandler)), Shared] [Method(LSP.Methods.TextDocumentCompletionName)] - internal class CompletionHandler : ILspServiceDocumentRequestHandler + internal sealed partial class CompletionHandler : ILspServiceDocumentRequestHandler { - internal const string EditRangeSetting = "editRange"; private readonly IGlobalOptionService _globalOptions; private readonly ImmutableHashSet _csharpTriggerCharacters; @@ -88,71 +82,10 @@ internal class CompletionHandler : ILspServiceDocumentRequestHandler(), - // If we have a suggestion mode item, we just need to keep the list in suggestion mode. - // We don't need to return the fake suggestion mode item. - SuggestionMode = list.SuggestionModeItem is not null, - IsIncomplete = isIncomplete, - }; - } - - var lspVSClientCapability = clientCapabilities.HasVisualStudioLspCapability() == true; - - var completionCapabilities = clientCapabilities.TextDocument?.Completion; - var supportedKinds = completionCapabilities?.CompletionItemKind?.ValueSet?.ToSet() ?? new HashSet(); - var snippetsSupported = completionCapabilities?.CompletionItem?.SnippetSupport ?? false; - var itemDefaultsSupported = completionCapabilities?.CompletionListSetting?.ItemDefaults?.Contains(EditRangeSetting) == true; - - // We use the first item in the completion list as our comparison point for span - // and range for optimization when generating the TextEdits later on. - var completionChange = await completionService.GetChangeAsync( - document, list.ItemsList[0], cancellationToken: cancellationToken).ConfigureAwait(false); - var defaultSpan = completionChange.TextChange.Span; - var defaultRange = ProtocolConversions.TextSpanToRange(defaultSpan, documentText); - - var supportsCompletionListData = clientCapabilities.HasCompletionListDataCapability(); - var completionResolveData = new CompletionResolveData() - { - ResultId = resultId, - }; - - var completionItemResolveData = supportsCompletionListData ? null : completionResolveData; - - using var _ = ArrayBuilder.GetInstance(out var lspCompletionItems); - var commitCharactersRuleCache = new Dictionary, string[]>(CommitCharacterArrayComparer.Instance); - - foreach (var item in list.ItemsList) - lspCompletionItems.Add(await CreateLSPCompletionItemAsync(item).ConfigureAwait(false)); - - var completionList = new LSP.VSInternalCompletionList - { - Items = lspCompletionItems.ToArray(), - SuggestionMode = list.SuggestionModeItem != null, - IsIncomplete = isIncomplete, - }; - - if (supportsCompletionListData) - completionList.Data = completionResolveData; - - if (clientCapabilities.HasCompletionListCommitCharactersCapability()) - PromoteCommonCommitCharactersOntoList(completionList); - - if (itemDefaultsSupported) - { - completionList.ItemDefaults = new LSP.CompletionListItemDefaults - { - EditRange = defaultRange, - }; - } - - return new LSP.OptimizedVSCompletionList(completionList); - - // Local functions + // Local function bool IsValidTriggerCharacterForDocument(Document document, char triggerCharacter) { if (document.Project.Language == LanguageNames.CSharp) @@ -168,150 +101,6 @@ bool IsValidTriggerCharacterForDocument(Document document, char triggerCharacter // Since we don't know what their trigger characters are, just return true. return true; } - - async Task CreateLSPCompletionItemAsync(CompletionItem item) - { - var creationService = document.Project.Solution.Services.GetRequiredService(); - - // Defer to host to create the actual completion item (including potential subclasses), and add any - // custom information. - var lspItem = await creationService.CreateAsync( - document, documentText, snippetsSupported, itemDefaultsSupported, defaultSpan, item, cancellationToken).ConfigureAwait(false); - - // Now add data common to all hosts. - lspItem.Data = completionItemResolveData; - lspItem.Label = $"{item.DisplayTextPrefix}{item.DisplayText}{item.DisplayTextSuffix}"; - - lspItem.SortText = item.SortText; - lspItem.FilterText = item.FilterText; - - lspItem.Kind = GetCompletionKind(item.Tags, supportedKinds); - lspItem.Preselect = ShouldItemBePreselected(item); - - lspItem.CommitCharacters = GetCommitCharacters(item, commitCharactersRuleCache, lspVSClientCapability); - - return lspItem; - } - - static LSP.CompletionItemKind GetCompletionKind( - ImmutableArray tags, - ISet supportedClientKinds) - { - foreach (var tag in tags) - { - if (ProtocolConversions.RoslynTagToCompletionItemKinds.TryGetValue(tag, out var completionItemKinds)) - { - // Always at least pick the core kind provided. - var kind = completionItemKinds[0]; - - // If better kinds are preferred, return them if the client supports them. - for (var i = 1; i < completionItemKinds.Length; i++) - { - var preferredKind = completionItemKinds[i]; - if (supportedClientKinds.Contains(preferredKind)) - kind = preferredKind; - } - - return kind; - } - } - - return LSP.CompletionItemKind.Text; - } - - static string[]? GetCommitCharacters( - CompletionItem item, - Dictionary, string[]> currentRuleCache, - bool supportsVSExtensions) - { - // VSCode does not have the concept of soft selection, the list is always hard selected. - // In order to emulate soft selection behavior for things like argument completion, regex completion, datetime completion, etc - // we create a completion item without any specific commit characters. This means only tab / enter will commit. - // VS supports soft selection, so we only do this for non-VS clients. - if (!supportsVSExtensions && item.Rules.SelectionBehavior == CompletionItemSelectionBehavior.SoftSelection) - return Array.Empty(); - - var commitCharacterRules = item.Rules.CommitCharacterRules; - - // VS will use the default commit characters if no items are specified on the completion item. - // However, other clients like VSCode do not support this behavior so we must specify - // commit characters on every completion item - https://github.com/microsoft/vscode/issues/90987 - if (supportsVSExtensions && commitCharacterRules.IsEmpty) - return null; - - if (!currentRuleCache.TryGetValue(commitCharacterRules, out var cachedCommitCharacters)) - { - using var _ = PooledHashSet.GetInstance(out var commitCharacters); - commitCharacters.AddAll(CompletionRules.Default.DefaultCommitCharacters); - foreach (var rule in commitCharacterRules) - { - switch (rule.Kind) - { - case CharacterSetModificationKind.Add: - commitCharacters.UnionWith(rule.Characters); - continue; - case CharacterSetModificationKind.Remove: - commitCharacters.ExceptWith(rule.Characters); - continue; - case CharacterSetModificationKind.Replace: - commitCharacters.Clear(); - commitCharacters.AddRange(rule.Characters); - break; - } - } - - cachedCommitCharacters = commitCharacters.Select(c => c.ToString()).ToArray(); - currentRuleCache.Add(item.Rules.CommitCharacterRules, cachedCommitCharacters); - } - - return cachedCommitCharacters; - } - - static void PromoteCommonCommitCharactersOntoList(LSP.VSInternalCompletionList completionList) - { - if (completionList.Items.IsEmpty()) - { - return; - } - - var defaultCommitCharacters = CompletionRules.Default.DefaultCommitCharacters.Select(c => c.ToString()).ToArray(); - var commitCharacterReferences = new Dictionary(); - var mostUsedCount = 0; - string[]? mostUsedCommitCharacters = null; - for (var i = 0; i < completionList.Items.Length; i++) - { - var completionItem = completionList.Items[i]; - var commitCharacters = completionItem.CommitCharacters; - // The commit characters on the item are null, this means the commit characters are actually - // the default commit characters we passed in the initialize request. - commitCharacters ??= defaultCommitCharacters; - - commitCharacterReferences.TryGetValue(commitCharacters, out var existingCount); - existingCount++; - - if (existingCount > mostUsedCount) - { - // Capture the most used commit character counts so we don't need to re-iterate the array later - mostUsedCommitCharacters = commitCharacters; - mostUsedCount = existingCount; - } - - commitCharacterReferences[commitCharacters] = existingCount; - } - - Contract.ThrowIfNull(mostUsedCommitCharacters); - - // Promoted the most used commit characters onto the list and then remove these from child items. - completionList.CommitCharacters = mostUsedCommitCharacters; - for (var i = 0; i < completionList.Items.Length; i++) - { - var completionItem = completionList.Items[i]; - if (completionItem.CommitCharacters == mostUsedCommitCharacters) - { - completionItem.CommitCharacters = null; - } - } - } } private async Task<(CompletionList CompletionList, bool IsIncomplete, long ResultId)?> GetFilteredCompletionListAsync( @@ -324,7 +113,6 @@ static void PromoteCommonCommitCharactersOntoList(LSP.VSInternalCompletionList c CancellationToken cancellationToken) { var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); - var completionListSpan = completionService.GetDefaultCompletionListSpan(sourceText, position); var completionTrigger = await ProtocolConversions.LSPToRoslynCompletionTriggerAsync(request.Context, document, position, cancellationToken).ConfigureAwait(false); var isTriggerForIncompleteCompletions = request.Context?.TriggerKind == LSP.CompletionTriggerKind.TriggerForIncompleteCompletions; var completionListCache = context.GetRequiredLspService(); @@ -351,7 +139,7 @@ static void PromoteCommonCommitCharactersOntoList(LSP.VSInternalCompletionList c var resultId = result.Value.ResultId; var completionListMaxSize = _globalOptions.GetOption(LspOptionsStorage.MaxCompletionListSize); - var (completionList, isIncomplete) = FilterCompletionList(result.Value.List, completionListMaxSize, completionListSpan, completionTrigger, sourceText); + var (completionList, isIncomplete) = FilterCompletionList(result.Value.List, completionListMaxSize, completionTrigger, sourceText); return (completionList, isIncomplete, resultId); } @@ -382,11 +170,10 @@ static void PromoteCommonCommitCharactersOntoList(LSP.VSInternalCompletionList c private static (CompletionList CompletionList, bool IsIncomplete) FilterCompletionList( CompletionList completionList, int completionListMaxSize, - TextSpan completionListSpan, CompletionTrigger completionTrigger, SourceText sourceText) { - var filterText = sourceText.GetSubText(completionListSpan).ToString(); + var filterText = sourceText.GetSubText(completionList.Span).ToString(); // Use pattern matching to determine which items are most relevant out of the calculated items. using var _ = ArrayBuilder.GetInstance(out var matchResultsBuilder); @@ -473,53 +260,5 @@ internal CompletionOptions GetCompletionOptions(Document document) TriggerInArgumentLists = false }; } - - private class CommitCharacterArrayComparer : IEqualityComparer> - { - public static readonly CommitCharacterArrayComparer Instance = new(); - - private CommitCharacterArrayComparer() - { - } - - public bool Equals([AllowNull] ImmutableArray x, [AllowNull] ImmutableArray y) - { - if (x == y) - return true; - - for (var i = 0; i < x.Length; i++) - { - var xKind = x[i].Kind; - var yKind = y[i].Kind; - if (xKind != yKind) - { - return false; - } - - var xCharacters = x[i].Characters; - var yCharacters = y[i].Characters; - if (xCharacters.Length != yCharacters.Length) - { - return false; - } - - for (var j = 0; j < xCharacters.Length; j++) - { - if (xCharacters[j] != yCharacters[j]) - { - return false; - } - } - } - - return true; - } - - public int GetHashCode([DisallowNull] ImmutableArray obj) - { - var combinedHash = Hash.CombineValues(obj); - return combinedHash; - } - } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler_CreateLspList.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler_CreateLspList.cs new file mode 100644 index 0000000000000..c760e5f59c2c2 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler_CreateLspList.cs @@ -0,0 +1,281 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; +using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler +{ + internal sealed partial class CompletionHandler + { + internal const string EditRangeSetting = "editRange"; + + private static async Task ConvertToLspCompletionListAsync( + Document document, + LSP.ClientCapabilities clientCapabilities, + CompletionList list, bool isIncomplete, long resultId, + CancellationToken cancellationToken) + { + if (list.ItemsList.Count == 0) + { + return new LSP.VSInternalCompletionList + { + Items = Array.Empty(), + // If we have a suggestion mode item, we just need to keep the list in suggestion mode. + // We don't need to return the fake suggestion mode item. + SuggestionMode = list.SuggestionModeItem is not null, + IsIncomplete = isIncomplete, + }; + } + + var lspVSClientCapability = clientCapabilities.HasVisualStudioLspCapability() == true; + + var completionCapabilities = clientCapabilities.TextDocument?.Completion; + var supportedKinds = completionCapabilities?.CompletionItemKind?.ValueSet?.ToSet() ?? new HashSet(); + var itemDefaultsSupported = completionCapabilities?.CompletionListSetting?.ItemDefaults?.Contains(EditRangeSetting) == true; + + // We use the default completion list span as our comparison point for optimization when generating the TextEdits later on. + var defaultSpan = list.Span; + var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var defaultRange = ProtocolConversions.TextSpanToRange(defaultSpan, documentText); + + // Set resolve data on list if the client supports it, otherwise set it on each item. + var resolveData = new CompletionResolveData() { ResultId = resultId }; + var (completionItemResolveData, completionListResolvedData) = clientCapabilities.HasCompletionListDataCapability() + ? (null as CompletionResolveData, resolveData) + : (resolveData, null); + + using var _ = ArrayBuilder.GetInstance(out var lspCompletionItems); + var commitCharactersRuleCache = new Dictionary, string[]>(CommitCharacterArrayComparer.Instance); + + foreach (var item in list.ItemsList) + lspCompletionItems.Add(await CreateLSPCompletionItemAsync(item).ConfigureAwait(false)); + + var completionList = new LSP.VSInternalCompletionList + { + Items = lspCompletionItems.ToArray(), + SuggestionMode = list.SuggestionModeItem != null, + IsIncomplete = isIncomplete, + Data = completionListResolvedData, + }; + + if (clientCapabilities.HasCompletionListCommitCharactersCapability()) + PromoteCommonCommitCharactersOntoList(completionList); + + if (itemDefaultsSupported) + { + completionList.ItemDefaults = new LSP.CompletionListItemDefaults + { + EditRange = defaultRange, + }; + } + + return new LSP.OptimizedVSCompletionList(completionList); + + async Task CreateLSPCompletionItemAsync(CompletionItem item) + { + var snippetsSupported = completionCapabilities?.CompletionItem?.SnippetSupport ?? false; + var creationService = document.Project.Solution.Services.GetRequiredService(); + + // Defer to host to create the actual completion item (including potential subclasses), and add any + // custom information. + var lspItem = await creationService.CreateAsync( + document, documentText, snippetsSupported, itemDefaultsSupported, defaultSpan, item, cancellationToken).ConfigureAwait(false); + + // Now add data common to all hosts. + lspItem.Data = completionItemResolveData; + lspItem.Label = $"{item.DisplayTextPrefix}{item.DisplayText}{item.DisplayTextSuffix}"; + + lspItem.SortText = item.SortText; + lspItem.FilterText = item.FilterText; + + lspItem.Kind = GetCompletionKind(item.Tags, supportedKinds); + lspItem.Preselect = ShouldItemBePreselected(item); + + lspItem.CommitCharacters = GetCommitCharacters(item, commitCharactersRuleCache, lspVSClientCapability); + + return lspItem; + } + + static LSP.CompletionItemKind GetCompletionKind( + ImmutableArray tags, + ISet supportedClientKinds) + { + foreach (var tag in tags) + { + if (ProtocolConversions.RoslynTagToCompletionItemKinds.TryGetValue(tag, out var completionItemKinds)) + { + // Always at least pick the core kind provided. + var kind = completionItemKinds[0]; + + // If better kinds are preferred, return them if the client supports them. + for (var i = 1; i < completionItemKinds.Length; i++) + { + var preferredKind = completionItemKinds[i]; + if (supportedClientKinds.Contains(preferredKind)) + kind = preferredKind; + } + + return kind; + } + } + + return LSP.CompletionItemKind.Text; + } + + static string[]? GetCommitCharacters( + CompletionItem item, + Dictionary, string[]> currentRuleCache, + bool supportsVSExtensions) + { + // VSCode does not have the concept of soft selection, the list is always hard selected. + // In order to emulate soft selection behavior for things like argument completion, regex completion, datetime completion, etc + // we create a completion item without any specific commit characters. This means only tab / enter will commit. + // VS supports soft selection, so we only do this for non-VS clients. + if (!supportsVSExtensions && item.Rules.SelectionBehavior == CompletionItemSelectionBehavior.SoftSelection) + return Array.Empty(); + + var commitCharacterRules = item.Rules.CommitCharacterRules; + + // VS will use the default commit characters if no items are specified on the completion item. + // However, other clients like VSCode do not support this behavior so we must specify + // commit characters on every completion item - https://github.com/microsoft/vscode/issues/90987 + if (supportsVSExtensions && commitCharacterRules.IsEmpty) + return null; + + if (!currentRuleCache.TryGetValue(commitCharacterRules, out var cachedCommitCharacters)) + { + using var _ = PooledHashSet.GetInstance(out var commitCharacters); + commitCharacters.AddAll(CompletionRules.Default.DefaultCommitCharacters); + foreach (var rule in commitCharacterRules) + { + switch (rule.Kind) + { + case CharacterSetModificationKind.Add: + commitCharacters.UnionWith(rule.Characters); + continue; + case CharacterSetModificationKind.Remove: + commitCharacters.ExceptWith(rule.Characters); + continue; + case CharacterSetModificationKind.Replace: + commitCharacters.Clear(); + commitCharacters.AddRange(rule.Characters); + break; + } + } + + cachedCommitCharacters = commitCharacters.Select(c => c.ToString()).ToArray(); + currentRuleCache.Add(item.Rules.CommitCharacterRules, cachedCommitCharacters); + } + + return cachedCommitCharacters; + } + + static void PromoteCommonCommitCharactersOntoList(LSP.VSInternalCompletionList completionList) + { + if (completionList.Items.IsEmpty()) + { + return; + } + + var defaultCommitCharacters = CompletionRules.Default.DefaultCommitCharacters.Select(c => c.ToString()).ToArray(); + var commitCharacterReferences = new Dictionary(); + var mostUsedCount = 0; + string[]? mostUsedCommitCharacters = null; + for (var i = 0; i < completionList.Items.Length; i++) + { + var completionItem = completionList.Items[i]; + var commitCharacters = completionItem.CommitCharacters; + // The commit characters on the item are null, this means the commit characters are actually + // the default commit characters we passed in the initialize request. + commitCharacters ??= defaultCommitCharacters; + + commitCharacterReferences.TryGetValue(commitCharacters, out var existingCount); + existingCount++; + + if (existingCount > mostUsedCount) + { + // Capture the most used commit character counts so we don't need to re-iterate the array later + mostUsedCommitCharacters = commitCharacters; + mostUsedCount = existingCount; + } + + commitCharacterReferences[commitCharacters] = existingCount; + } + + Contract.ThrowIfNull(mostUsedCommitCharacters); + + // Promoted the most used commit characters onto the list and then remove these from child items. + completionList.CommitCharacters = mostUsedCommitCharacters; + for (var i = 0; i < completionList.Items.Length; i++) + { + var completionItem = completionList.Items[i]; + if (completionItem.CommitCharacters == mostUsedCommitCharacters) + { + completionItem.CommitCharacters = null; + } + } + } + } + + private sealed class CommitCharacterArrayComparer : IEqualityComparer> + { + public static readonly CommitCharacterArrayComparer Instance = new(); + + private CommitCharacterArrayComparer() + { + } + + public bool Equals([AllowNull] ImmutableArray x, [AllowNull] ImmutableArray y) + { + if (x == y) + return true; + + for (var i = 0; i < x.Length; i++) + { + var xKind = x[i].Kind; + var yKind = y[i].Kind; + if (xKind != yKind) + { + return false; + } + + var xCharacters = x[i].Characters; + var yCharacters = y[i].Characters; + if (xCharacters.Length != yCharacters.Length) + { + return false; + } + + for (var j = 0; j < xCharacters.Length; j++) + { + if (xCharacters[j] != yCharacters[j]) + { + return false; + } + } + } + + return true; + } + + public int GetHashCode([DisallowNull] ImmutableArray obj) + { + var combinedHash = Hash.CombineValues(obj); + return combinedHash; + } + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs index a6a1eb1acf6fc..000bc5ea483b9 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CommonLanguageServerProtocol.Framework; -using Microsoft.VisualStudio.Text.Adornments; using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -21,10 +20,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { /// /// Handle a completion resolve request to add description. - /// - /// TODO - This must be moved to the MS.CA.LanguageServer.Protocol project once the - /// references to VS icon types and classified text runs are removed. - /// See https://github.com/dotnet/roslyn/issues/55142 /// /// /// This isn't a because it could return null. @@ -62,48 +57,24 @@ public async Task HandleRequestAsync(LSP.CompletionItem comp return completionItem; } - var list = cacheEntry.CompletionList; - // Find the matching completion item in the completion list - var selectedItem = list.ItemsList.FirstOrDefault(cachedCompletionItem => MatchesLSPCompletionItem(completionItem, cachedCompletionItem)); - if (selectedItem == null) - { - return completionItem; - } + var selectedItem = cacheEntry.CompletionList.ItemsList.FirstOrDefault(cachedCompletionItem => MatchesLSPCompletionItem(completionItem, cachedCompletionItem)); var completionOptions = _globalOptions.GetCompletionOptions(document.Project.Language); - var displayOptions = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); - var description = await completionService.GetDescriptionAsync(document, selectedItem, completionOptions, displayOptions, cancellationToken).ConfigureAwait(false)!; - if (description != null) - { - var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability(); - if (supportsVSExtensions) - { - var vsCompletionItem = (LSP.VSInternalCompletionItem)completionItem; - vsCompletionItem.Description = new ClassifiedTextElement(description.TaggedParts - .Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); - } - else - { - var clientSupportsMarkdown = clientCapabilities.TextDocument?.Completion?.CompletionItem?.DocumentationFormat?.Contains(LSP.MarkupKind.Markdown) == true; - completionItem.Documentation = ProtocolConversions.GetDocumentationMarkupContent(description.TaggedParts, document, clientSupportsMarkdown); - } - } + var symbolDescriptionOptions = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); - // We compute the TextEdit resolves for complex text edits (e.g. override and partial - // method completions) here. Lazily resolving TextEdits is technically a violation of - // the LSP spec, but is currently supported by the VS client anyway. Once the VS client - // adheres to the spec, this logic will need to change and VS will need to provide - // official support for TextEdit resolution in some form. - if (selectedItem.IsComplexTextEdit) + if (selectedItem is not null) { - Contract.ThrowIfTrue(completionItem.InsertText != null); - Contract.ThrowIfTrue(completionItem.TextEdit != null); - - var snippetsSupported = clientCapabilities?.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false; - - completionItem.TextEdit = await GenerateTextEditAsync( - document, completionService, selectedItem, snippetsSupported, cancellationToken).ConfigureAwait(false); + var creationService = document.Project.Solution.Services.GetRequiredService(); + await creationService.ResolveAsync( + completionItem, + selectedItem, + document, + clientCapabilities, + completionService, + completionOptions, + symbolDescriptionOptions, + cancellationToken).ConfigureAwait(false); } return completionItem; @@ -131,52 +102,6 @@ private static bool MatchesLSPCompletionItem(LSP.CompletionItem lspCompletionIte return string.Equals(originalDisplayText, completionItem.DisplayText); } - // Internal for testing - internal static async Task GenerateTextEditAsync( - Document document, - CompletionService completionService, - CompletionItem selectedItem, - bool snippetsSupported, - CancellationToken cancellationToken) - { - var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - - var completionChange = await completionService.GetChangeAsync( - document, selectedItem, cancellationToken: cancellationToken).ConfigureAwait(false); - var completionChangeSpan = completionChange.TextChange.Span; - var newText = completionChange.TextChange.NewText; - Contract.ThrowIfNull(newText); - - // If snippets are supported, that means we can move the caret (represented by $0) to - // a new location. - if (snippetsSupported) - { - var caretPosition = completionChange.NewPosition; - if (caretPosition.HasValue) - { - // caretPosition is the absolute position of the caret in the document. - // We want the position relative to the start of the snippet. - var relativeCaretPosition = caretPosition.Value - completionChangeSpan.Start; - - // The caret could technically be placed outside the bounds of the text - // being inserted. This situation is currently unsupported in LSP, so in - // these cases we won't move the caret. - if (relativeCaretPosition >= 0 && relativeCaretPosition <= newText.Length) - { - newText = newText.Insert(relativeCaretPosition, "$0"); - } - } - } - - var textEdit = new LSP.TextEdit() - { - NewText = newText, - Range = ProtocolConversions.TextSpanToRange(completionChangeSpan, documentText), - }; - - return textEdit; - } - private CompletionListCache.CacheEntry? GetCompletionListCacheEntry(LSP.CompletionItem request) { Contract.ThrowIfNull(request.Data); diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs index 78c499ba89c3b..b955a56962ec4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs @@ -4,13 +4,17 @@ using System; using System.Composition; +using System.Diagnostics; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Completion @@ -25,6 +29,16 @@ internal interface ILspCompletionResultCreationService : IWorkspaceService TextSpan defaultSpan, CompletionItem item, CancellationToken cancellationToken); + + Task ResolveAsync( + LSP.CompletionItem completionItem, + CompletionItem selectedItem, + Document document, + LSP.ClientCapabilities clientCapabilities, + CompletionService completionService, + CompletionOptions completionOptions, + SymbolDescriptionOptions symbolDescriptionOptions, + CancellationToken cancellationToken); } [ExportWorkspaceService(typeof(ILspCompletionResultCreationService)), Shared] @@ -46,9 +60,26 @@ public DefaultLspCompletionResultCreationService() CancellationToken cancellationToken) { var completionItem = new LSP.CompletionItem(); + await PopulateTextEditAsync(document, documentText, itemDefaultsSupported, defaultSpan, item, completionItem, cancellationToken).ConfigureAwait(false); + return completionItem; + } - await PopulateTextEditAsync( - document, documentText, itemDefaultsSupported, defaultSpan, item, completionItem, cancellationToken).ConfigureAwait(false); + public async Task ResolveAsync( + LSP.CompletionItem completionItem, + CompletionItem selectedItem, + Document document, + LSP.ClientCapabilities clientCapabilities, + CompletionService completionService, + CompletionOptions completionOptions, + SymbolDescriptionOptions symbolDescriptionOptions, + CancellationToken cancellationToken) + { + var description = await completionService.GetDescriptionAsync(document, selectedItem, completionOptions, symbolDescriptionOptions, cancellationToken).ConfigureAwait(false)!; + if (description != null) + { + var clientSupportsMarkdown = clientCapabilities.TextDocument?.Completion?.CompletionItem?.DocumentationFormat?.Contains(LSP.MarkupKind.Markdown) == true; + completionItem.Documentation = ProtocolConversions.GetDocumentationMarkupContent(description.TaggedParts, document, clientSupportsMarkdown); + } return completionItem; } @@ -77,6 +108,7 @@ public DefaultLspCompletionResultCreationService() } else { + Debug.Assert(completionChangeSpan == defaultSpan || item.IsComplexTextEdit); lspItem.TextEdit = new LSP.TextEdit() { NewText = newText, diff --git a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OptionList.cs b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OptionList.cs index 2836f42693668..ba6470f52af46 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OptionList.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OptionList.cs @@ -24,6 +24,7 @@ internal partial class DidChangeConfigurationNotificationHandler ImplementTypeOptionsStorage.PropertyGenerationBehavior, // Completion CompletionOptionsStorage.ShowNameSuggestions, + CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, CompletionOptionsStorage.ProvideRegexCompletions, QuickInfoOptionsStorage.ShowRemarksInQuickInfo, // Go to definition diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs index 8b5355ecf1c08..537efcb3ef13e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/ProjectDiagnosticSource.cs @@ -30,7 +30,8 @@ internal sealed record class ProjectDiagnosticSource(Project Project) : IDiagnos // we're passing in. If information is already cached for that snapshot, it will be returned. Otherwise, // it will be computed on demand. Because it is always accurate as per this snapshot, all spans are correct // and do not need to be adjusted. - var projectDiagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync(Project.Solution, Project.Id, cancellationToken: cancellationToken).ConfigureAwait(false); + var projectDiagnostics = await diagnosticAnalyzerService.GetProjectDiagnosticsForIdsAsync(Project.Solution, Project.Id, + diagnosticIds: null, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); return projectDiagnostics; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs index 2abdd42726a88..faa4874e2cda6 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DiagnosticSources/WorkspaceDocumentDiagnosticSource.cs @@ -35,7 +35,8 @@ public WorkspaceDocumentDiagnosticSource(TextDocument document) // including those reported as a compilation end diagnostic. These are not included in document pull (uses GetDiagnosticsForSpan) due to cost. // However we can include them as a part of workspace pull when FSA is on. var documentDiagnostics = await diagnosticAnalyzerService.GetDiagnosticsForIdsAsync( - Document.Project.Solution, Document.Project.Id, Document.Id, cancellationToken: cancellationToken).ConfigureAwait(false); + Document.Project.Solution, Document.Project.Id, Document.Id, + diagnosticIds: null, includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, cancellationToken).ConfigureAwait(false); return documentDiagnostics; } } diff --git a/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs index ef593aaa44073..c4f1788951549 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/ILspServiceRequestHandler.cs @@ -7,6 +7,9 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; +/// +/// Handler for a workspace request with parameters and result . +/// internal interface ILspServiceRequestHandler : ILspService, IRequestHandler, @@ -14,6 +17,19 @@ internal interface ILspServiceRequestHandler : { } +/// +/// Handler for a workspace parameter-less request with result . +/// +internal interface ILspServiceRequestHandler : + ILspService, + IRequestHandler, + ISolutionRequiredHandler +{ +} + +/// +/// Handler for document request with parameters and result . +/// internal interface ILspServiceDocumentRequestHandler : ILspServiceRequestHandler, ITextDocumentIdentifierHandler, diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs index 9875aa41296e2..aa98addfc4f9b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs @@ -18,5 +18,5 @@ public InlayHintCache() : base(maxCacheSize: 3) /// /// Cached data need to resolve a specific inlay hint item. /// - internal record InlayHintCacheEntry(ImmutableArray InlayHintMembers, TextDocumentIdentifier TextDocumentIdentifier, VersionStamp SyntaxVersion); + internal record InlayHintCacheEntry(ImmutableArray InlayHintMembers, VersionStamp SyntaxVersion); } diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 41d61987595ed..680a680b6845b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -56,7 +56,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) // Store the members in the resolve cache so that when we get a resolve request for a particular // member we can re-use the inline hint. - var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints, request.TextDocument, syntaxVersion)); + var resultId = inlayHintCache.UpdateCache(new InlayHintCache.InlayHintCacheEntry(hints, syntaxVersion)); for (var i = 0; i < hints.Length; i++) { @@ -85,7 +85,7 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(InlayHintParams request) ToolTip = null, PaddingLeft = leftPadding, PaddingRight = rightPadding, - Data = new InlayHintResolveData(resultId, i) + Data = new InlayHintResolveData(resultId, i, request.TextDocument) }; inlayHints.Add(inlayHint); diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs index 70546bd0bd83f..ea549b4facc50 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveData.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.VisualStudio.LanguageServer.Protocol; + namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; /// @@ -9,4 +11,5 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; /// /// the resultId associated with the inlay hint created on original request. /// the index of the specific inlay hint item in the original list. -internal sealed record InlayHintResolveData(long ResultId, int ListIndex); +/// /// the text document associated with the inlay hint to resolve. +internal sealed record InlayHintResolveData(long ResultId, int ListIndex, TextDocumentIdentifier TextDocument); diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 5c627f36bf7af..cad8944a8c64f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.InlineHints; +using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; using Microsoft.VisualStudio.LanguageServer.Protocol; using Newtonsoft.Json.Linq; using Roslyn.Utilities; @@ -32,12 +33,13 @@ public InlayHintResolveHandler(InlayHintCache inlayHintCache) public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) - => GetCacheEntry(request).CacheEntry.TextDocumentIdentifier; + => GetInlayHintResolveData(request).TextDocument; public async Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); - var (cacheEntry, inlineHintToResolve) = GetCacheEntry(request); + var resolveData = GetInlayHintResolveData(request); + var (cacheEntry, inlineHintToResolve) = GetCacheEntry(resolveData); var currentSyntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); var cachedSyntaxVersion = cacheEntry.SyntaxVersion; @@ -56,14 +58,18 @@ public async Task HandleRequestAsync(LSP.InlayHint request, Reque return request; } - private (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(LSP.InlayHint request) + private (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData) { - var resolveData = (request.Data as JToken)?.ToObject(); - Contract.ThrowIfNull(resolveData, "Missing data for inlay hint resolve request"); - var cacheEntry = _inlayHintCache.GetCachedEntry(resolveData.ResultId); Contract.ThrowIfNull(cacheEntry, "Missing cache entry for inlay hint resolve request"); return (cacheEntry, cacheEntry.InlayHintMembers[resolveData.ListIndex]); } + + private static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) + { + var resolveData = (inlayHint.Data as JToken)?.ToObject(); + Contract.ThrowIfNull(resolveData, "Missing data for inlay hint resolve request"); + return resolveData; + } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs b/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs index 9bd940b99b0da..710c19d638864 100644 --- a/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs +++ b/src/Features/LanguageServer/Protocol/Handler/LanguageServerNotificationManager.cs @@ -26,11 +26,15 @@ public ClientLanguageServerManager(JsonRpc jsonRpc) public Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken) => _jsonRpc.InvokeWithParameterObjectAsync(methodName, @params, cancellationToken); + public async ValueTask SendRequestAsync(string methodName, CancellationToken cancellationToken) + => await _jsonRpc.InvokeWithCancellationAsync(methodName, cancellationToken: cancellationToken).ConfigureAwait(false); + + public async ValueTask SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken) + => await _jsonRpc.InvokeWithParameterObjectAsync(methodName, @params, cancellationToken).ConfigureAwait(false); + public async ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken) => await _jsonRpc.NotifyAsync(methodName).ConfigureAwait(false); public async ValueTask SendNotificationAsync(string methodName, TParams @params, CancellationToken cancellationToken) - { - await _jsonRpc.NotifyWithParameterObjectAsync(methodName, @params).ConfigureAwait(false); - } + => await _jsonRpc.NotifyWithParameterObjectAsync(methodName, @params).ConfigureAwait(false); } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs index a0b3d4e8002e3..eea99ee5fabae 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; @@ -22,62 +21,18 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens { internal class SemanticTokensHelpers { - /// - /// Maps an LSP token type to the index LSP associates with the token. - /// Required since we report tokens back to LSP as a series of ints, - /// and LSP needs a way to decipher them. - /// - public static readonly Dictionary TokenTypeToIndex; - - // TO-DO: Expand this mapping once support for custom token types is added: - // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1085998 - internal static readonly Dictionary ClassificationTypeToSemanticTokenTypeMap = - new() - { - [ClassificationTypeNames.Comment] = LSP.SemanticTokenTypes.Comment, - [ClassificationTypeNames.Identifier] = LSP.SemanticTokenTypes.Variable, - [ClassificationTypeNames.Keyword] = LSP.SemanticTokenTypes.Keyword, - [ClassificationTypeNames.NumericLiteral] = LSP.SemanticTokenTypes.Number, - [ClassificationTypeNames.Operator] = LSP.SemanticTokenTypes.Operator, - [ClassificationTypeNames.StringLiteral] = LSP.SemanticTokenTypes.String, - }; - - public static readonly ImmutableArray RoslynCustomTokenTypes = ClassificationTypeNames.AllTypeNames - .Where( - type => !ClassificationTypeToSemanticTokenTypeMap.ContainsKey(type) && - !ClassificationTypeNames.AdditiveTypeNames.Contains(type)).Order().ToImmutableArray(); - - public static readonly ImmutableArray AllTokenTypes = SemanticTokenTypes.AllTypes.Concat(RoslynCustomTokenTypes).ToImmutableArray(); - - static SemanticTokensHelpers() - { - // Computes the mapping between a LSP token type and its respective index recognized by LSP. - TokenTypeToIndex = new Dictionary(); - var index = 0; - foreach (var lspTokenType in LSP.SemanticTokenTypes.AllTypes) - { - TokenTypeToIndex.Add(lspTokenType, index); - index++; - } - - foreach (var roslynTokenType in RoslynCustomTokenTypes) - { - TokenTypeToIndex.Add(roslynTokenType, index); - index++; - } - } - /// /// Returns the semantic tokens data for a given document with an optional range. /// - internal static async Task ComputeSemanticTokensDataAsync( + public static async Task ComputeSemanticTokensDataAsync( + ClientCapabilities capabilities, Document document, - Dictionary tokenTypesToIndex, LSP.Range? range, ClassificationOptions options, - bool includeSyntacticClassifications, CancellationToken cancellationToken) { + var tokenTypesToIndex = SemanticTokensSchema.GetSchema(capabilities).TokenTypeToIndex; + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -86,7 +41,7 @@ static SemanticTokensHelpers() var textSpan = range is null ? root.FullSpan : ProtocolConversions.RangeToTextSpan(range, text); var classifiedSpans = await GetClassifiedSpansForDocumentAsync( - document, textSpan, options, includeSyntacticClassifications, cancellationToken).ConfigureAwait(false); + document, textSpan, options, cancellationToken).ConfigureAwait(false); // Multi-line tokens are not supported by VS (tracked by https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1265495). // Roslyn's classifier however can return multi-line classified spans, so we must break these up into single-line spans. @@ -94,48 +49,31 @@ static SemanticTokensHelpers() // TO-DO: We should implement support for streaming if LSP adds support for it: // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1276300 - return ComputeTokens(text.Lines, updatedClassifiedSpans, tokenTypesToIndex); + return ComputeTokens(capabilities, text.Lines, updatedClassifiedSpans, tokenTypesToIndex); } private static async Task GetClassifiedSpansForDocumentAsync( Document document, TextSpan textSpan, ClassificationOptions options, - bool includeSyntacticClassifications, CancellationToken cancellationToken) { var classificationService = document.GetRequiredLanguageService(); using var _ = ArrayBuilder.GetInstance(out var classifiedSpans); - // Case 1 - Generated Razor documents: - // In Razor, the C# syntax classifier does not run on the client. This means we need to return both - // syntactic and semantic classifications. - // Case 2 - C# and VB documents: - // In C#/VB, the syntax classifier runs on the client. This means we only need to return semantic - // classifications. - // - // Ideally, Razor will eventually run the classifier on their end so we can get rid of this special - // casing: https://github.com/dotnet/razor-tooling/issues/5850 - if (includeSyntacticClassifications) - { - // `includeAdditiveSpans` will add token modifiers such as 'static', which we want to include in LSP. - var spans = await ClassifierHelper.GetClassifiedSpansAsync( - document, textSpan, options, includeAdditiveSpans: true, cancellationToken).ConfigureAwait(false); - - // The spans returned to us may include some empty spans, which we don't care about. We also don't care - // about the 'text' classification. It's added for everything between real classifications (including - // whitespace), and just means 'don't classify this'. No need for us to actually include that in - // semantic tokens as it just wastes space in the result. - var nonEmptySpans = spans.Where(s => !s.TextSpan.IsEmpty && s.ClassificationType != ClassificationTypeNames.Text); - classifiedSpans.AddRange(nonEmptySpans); - } - else - { - await classificationService.AddSemanticClassificationsAsync( - document, textSpan, options, classifiedSpans, cancellationToken).ConfigureAwait(false); - await classificationService.AddEmbeddedLanguageClassificationsAsync( - document, textSpan, options, classifiedSpans, cancellationToken).ConfigureAwait(false); - } + // We always return both syntactic and semantic classifications. If there is a syntactic classifier running on the client + // then the semantic token classifications will override them. + + // `includeAdditiveSpans` will add token modifiers such as 'static', which we want to include in LSP. + var spans = await ClassifierHelper.GetClassifiedSpansAsync( + document, textSpan, options, includeAdditiveSpans: true, cancellationToken).ConfigureAwait(false); + + // The spans returned to us may include some empty spans, which we don't care about. We also don't care + // about the 'text' classification. It's added for everything between real classifications (including + // whitespace), and just means 'don't classify this'. No need for us to actually include that in + // semantic tokens as it just wastes space in the result. + var nonEmptySpans = spans.Where(s => !s.TextSpan.IsEmpty && s.ClassificationType != ClassificationTypeNames.Text); + classifiedSpans.AddRange(nonEmptySpans); // Classified spans are not guaranteed to be returned in a certain order so we sort them to be safe. classifiedSpans.Sort(ClassifiedSpanComparer.Instance); @@ -232,9 +170,10 @@ public static ClassifiedSpan[] ConvertMultiLineToSingleLineSpans(SourceText text } private static int[] ComputeTokens( + ClientCapabilities capabilities, TextLineCollection lines, ClassifiedSpan[] classifiedSpans, - Dictionary tokenTypesToIndex) + IReadOnlyDictionary tokenTypesToIndex) { using var _ = ArrayBuilder.GetInstance(classifiedSpans.Length, out var data); @@ -243,11 +182,13 @@ public static ClassifiedSpan[] ConvertMultiLineToSingleLineSpans(SourceText text var lastLineNumber = 0; var lastStartCharacter = 0; + var tokenTypeMap = SemanticTokensSchema.GetSchema(capabilities).TokenTypeMap; + for (var currentClassifiedSpanIndex = 0; currentClassifiedSpanIndex < classifiedSpans.Length; currentClassifiedSpanIndex++) { currentClassifiedSpanIndex = ComputeNextToken( lines, ref lastLineNumber, ref lastStartCharacter, classifiedSpans, - currentClassifiedSpanIndex, tokenTypesToIndex, + currentClassifiedSpanIndex, tokenTypeMap, tokenTypesToIndex, out var deltaLine, out var startCharacterDelta, out var tokenLength, out var tokenType, out var tokenModifiers); @@ -263,7 +204,8 @@ public static ClassifiedSpan[] ConvertMultiLineToSingleLineSpans(SourceText text ref int lastStartCharacter, ClassifiedSpan[] classifiedSpans, int currentClassifiedSpanIndex, - Dictionary tokenTypesToIndex, + IReadOnlyDictionary tokenTypeMap, + IReadOnlyDictionary tokenTypesToIndex, out int deltaLineOut, out int startCharacterDeltaOut, out int tokenLengthOut, @@ -324,7 +266,7 @@ public static ClassifiedSpan[] ConvertMultiLineToSingleLineSpans(SourceText text { // 6. Token type - looked up in SemanticTokensLegend.tokenTypes (language server defined mapping // from integer to LSP token types). - tokenTypeIndex = GetTokenTypeIndex(classificationType, tokenTypesToIndex); + tokenTypeIndex = GetTokenTypeIndex(classificationType); } // Break out of the loop if we have no more classified spans left, or if the next classified span has @@ -344,17 +286,17 @@ public static ClassifiedSpan[] ConvertMultiLineToSingleLineSpans(SourceText text tokenModifiersOut = (int)modifierBits; return currentClassifiedSpanIndex; - } - private static int GetTokenTypeIndex(string classificationType, Dictionary tokenTypesToIndex) - { - if (!ClassificationTypeToSemanticTokenTypeMap.TryGetValue(classificationType, out var tokenTypeStr)) + int GetTokenTypeIndex(string classificationType) { - tokenTypeStr = classificationType; - } + if (!tokenTypeMap.TryGetValue(classificationType, out var tokenTypeStr)) + { + tokenTypeStr = classificationType; + } - Contract.ThrowIfFalse(tokenTypesToIndex.TryGetValue(tokenTypeStr, out var tokenTypeIndex), "No matching token type index found."); - return tokenTypeIndex; + Contract.ThrowIfFalse(tokenTypesToIndex.TryGetValue(tokenTypeStr, out var tokenTypeIndex), "No matching token type index found."); + return tokenTypeIndex; + } } private class ClassifiedSpanComparer : IComparer diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs index 0f50523e03e6c..8cc34d655ca22 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs @@ -55,12 +55,12 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.SemanticTokensRangeP // partial token results. In addition, a range request is only ever called with a whole // document request, so caching range results is unnecessary since the whole document // handler will cache the results anyway. + var capabilities = context.GetRequiredClientCapabilities(); var tokensData = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + capabilities, document, - SemanticTokensHelpers.TokenTypeToIndex, request.Range, options, - includeSyntacticClassifications: contextDocument.IsRazorDocument(), cancellationToken).ConfigureAwait(false); // The above call to get semantic tokens may be inaccurate (because we use frozen partial semantics). Kick diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs index 2e9d23c2f9e0c..97aed40551f19 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRefreshQueue.cs @@ -131,7 +131,7 @@ public async Task TryEnqueueRefreshComputationAsync(Project project, Cancellatio { if (documentUri is null || !trackedDocuments.ContainsKey(documentUri)) { - return notificationManager.SendNotificationAsync(Methods.WorkspaceSemanticTokensRefreshName, cancellationToken); + return notificationManager.SendRequestAsync(Methods.WorkspaceSemanticTokensRefreshName, cancellationToken); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.cs new file mode 100644 index 0000000000000..4ce8468d96c9d --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensSchema.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.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens +{ + internal readonly struct SemanticTokensSchema + { + // TO-DO: Expand this mapping once support for custom token types is added: + // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1085998 + + /// + /// Core VS classifications, only map a few things to LSP. The rest we keep as our own standard classification + /// type names so those continue to work in VS. + /// + private static readonly SemanticTokensSchema s_vsTokenSchema = new( + new Dictionary + { + [ClassificationTypeNames.Comment] = SemanticTokenTypes.Comment, + [ClassificationTypeNames.Identifier] = SemanticTokenTypes.Variable, + [ClassificationTypeNames.Keyword] = SemanticTokenTypes.Keyword, + [ClassificationTypeNames.NumericLiteral] = SemanticTokenTypes.Number, + [ClassificationTypeNames.Operator] = SemanticTokenTypes.Operator, + [ClassificationTypeNames.StringLiteral] = SemanticTokenTypes.String, + }); + + /// + /// The 'pure' set of classification types maps exact roslyn matches to the well defined values actually in LSP. + /// For example "class name" to "class". Importantly though, if there is no exact match, we do not map things + /// along. This allows the user to theme things however they want. + /// + private static readonly SemanticTokensSchema s_pureLspTokenSchema = new( + s_vsTokenSchema.TokenTypeMap.Concat(new Dictionary + { + [ClassificationTypeNames.ClassName] = SemanticTokenTypes.Class, + [ClassificationTypeNames.StructName] = SemanticTokenTypes.Struct, + [ClassificationTypeNames.NamespaceName] = SemanticTokenTypes.Namespace, + [ClassificationTypeNames.EnumName] = SemanticTokenTypes.Enum, + [ClassificationTypeNames.InterfaceName] = SemanticTokenTypes.Interface, + [ClassificationTypeNames.TypeParameterName] = SemanticTokenTypes.TypeParameter, + [ClassificationTypeNames.ParameterName] = SemanticTokenTypes.Parameter, + [ClassificationTypeNames.LocalName] = SemanticTokenTypes.Variable, + [ClassificationTypeNames.PropertyName] = SemanticTokenTypes.Property, + [ClassificationTypeNames.MethodName] = SemanticTokenTypes.Method, + [ClassificationTypeNames.EnumMemberName] = SemanticTokenTypes.EnumMember, + [ClassificationTypeNames.EventName] = SemanticTokenTypes.Event, + [ClassificationTypeNames.PreprocessorKeyword] = SemanticTokenTypes.Macro, + // in https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#standard-token-types-and-modifiers + [ClassificationTypeNames.LabelName] = "label", + }).ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); + + /// + /// Mapping from roslyn to the LSP we + /// should use for them. If something is not mapped, we will pass along the roslyn type name along. + /// + public readonly IReadOnlyDictionary TokenTypeMap; + + /// + /// Mapping from classification name to the index in . Required since we report + /// tokens back to LSP as a series of ints, and LSP needs a way to decipher them. + /// + public readonly IReadOnlyDictionary TokenTypeToIndex; + + /// + /// The token types that Roslyn specifically defines for a particular client. + /// + public readonly ImmutableArray CustomTokenTypes; + + /// + /// Equivalent to and combined. + /// + public readonly ImmutableArray AllTokenTypes; + + public SemanticTokensSchema(IReadOnlyDictionary tokenTypeMap) + { + TokenTypeMap = tokenTypeMap; + + CustomTokenTypes = ClassificationTypeNames.AllTypeNames + .Where(type => !tokenTypeMap.ContainsKey(type) && !ClassificationTypeNames.AdditiveTypeNames.Contains(type)) + .Order() + .ToImmutableArray(); + + AllTokenTypes = SemanticTokenTypes.AllTypes.Concat(CustomTokenTypes).ToImmutableArray(); + + var tokenTypeToIndex = new Dictionary(); + + foreach (var lspTokenType in SemanticTokenTypes.AllTypes) + tokenTypeToIndex.Add(lspTokenType, tokenTypeToIndex.Count); + + foreach (var roslynTokenType in CustomTokenTypes) + tokenTypeToIndex.Add(roslynTokenType, tokenTypeToIndex.Count); + + TokenTypeToIndex = tokenTypeToIndex; + } + + public static SemanticTokensSchema GetSchema(ClientCapabilities capabilities) + => capabilities.HasVisualStudioLspCapability() + ? s_vsTokenSchema + : s_pureLspTokenSchema; + + public static SemanticTokensSchema LegacyTokenSchemaForRazor + => s_vsTokenSchema; + + public static SemanticTokensSchema LegacyTokensSchemaForLSIF + => s_vsTokenSchema; + } +} diff --git a/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs b/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs index bb4242b113ba4..961702e1ed279 100644 --- a/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs +++ b/src/Features/LanguageServer/Protocol/IClientLanguageServerManager.cs @@ -7,9 +7,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer; +/// +/// Manages sending requests or notifications to the client or server. +/// Note - be extremely intentional about using a request or notification. Use exactly what the LSP spec defines the method as. +/// For example methods defined as requests even with no parameters or return value must be sent as requests regardless. +/// internal interface IClientLanguageServerManager : ILspService { Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); + ValueTask SendRequestAsync(string methodName, CancellationToken cancellationToken); + ValueTask SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken); ValueTask SendNotificationAsync(string methodName, TParams @params, CancellationToken cancellationToken); } diff --git a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 660ce56e5ad47..658470c304ccb 100644 --- a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -33,15 +33,18 @@ + + + diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 073e0bc0b0915..75672973ab3b5 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -28,7 +28,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer /// Future work for this workspace includes supporting basic metadata references (mscorlib, System dlls, etc), /// but that is dependent on having a x-plat mechanism for retrieving those references from the framework / sdk. /// - internal class LspMiscellaneousFilesWorkspace : Workspace, ILspService + internal class LspMiscellaneousFilesWorkspace : Workspace, ILspService, ILspWorkspace { private static readonly LanguageInformation s_csharpLanguageInformation = new(LanguageNames.CSharp, ".csx"); private static readonly LanguageInformation s_vbLanguageInformation = new(LanguageNames.VisualBasic, ".vbx"); @@ -45,6 +45,8 @@ public LspMiscellaneousFilesWorkspace(HostServices hostServices) : base(hostServ { } + public bool SupportsMutation => true; + /// /// Takes in a file URI and text and creates a misc project and document for the file. /// @@ -94,6 +96,12 @@ public void TryRemoveMiscellaneousDocument(Uri uri) } } + public ValueTask UpdateTextIfPresentAsync(DocumentId documentId, SourceText sourceText, CancellationToken cancellationToken) + { + this.OnDocumentTextChanged(documentId, sourceText, PreservationMode.PreserveIdentity, requireDocumentPresent: false); + return ValueTaskFactory.CompletedTask; + } + private sealed class SourceTextLoader : TextLoader { private readonly SourceText _sourceText; diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index 4b385d858dd2e..c98d45fd59885 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -241,8 +241,9 @@ public async Task<(Workspace?, Solution?, Document?)> GetLspDocumentInfoAsync(Te } // We didn't find the document in any workspace, record a telemetry notification that we did not find it. + // Depending on the host, this can be entirely normal (e.g. opening a loose file) var searchedWorkspaceKinds = string.Join(";", lspSolutions.SelectAsArray(lspSolution => lspSolution.Solution.Workspace.Kind)); - _logger.LogError($"Could not find '{textDocumentIdentifier.Uri}'. Searched {searchedWorkspaceKinds}"); + _logger.LogInformation($"Could not find '{textDocumentIdentifier.Uri}'. Searched {searchedWorkspaceKinds}"); _requestTelemetryLogger.UpdateFindDocumentTelemetryData(success: false, workspaceKind: null); // Add the document to our loose files workspace (if we have one) if it iss open. diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs index deaa7dcf9b2f1..d5da73cb97b6a 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs @@ -136,6 +136,167 @@ void M() AssertJsonEquals(expectedResolvedAction, actualResolvedAction); } + [WpfTheory, CombinatorialData] + public async Task TestRename(bool mutatingLspWorkspace) + { + var markUp = @" +class {|caret:ABC|} +{ +}"; + + await using var testLspServer = await CreateTestLspServerAsync(markUp, mutatingLspWorkspace, new InitializationOptions + { + ClientCapabilities = new ClientCapabilities() + { + Workspace = new WorkspaceClientCapabilities + { + WorkspaceEdit = new WorkspaceEditSetting + { + ResourceOperations = new ResourceOperationKind[] { ResourceOperationKind.Rename } + } + } + } + }); + var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( + title: string.Format(FeaturesResources.Rename_file_to_0, "ABC.cs"), + kind: CodeActionKind.Refactor, + children: Array.Empty(), + data: CreateCodeActionResolveData( + string.Format(FeaturesResources.Rename_file_to_0, "ABC.cs"), + testLspServer.GetLocations("caret").Single()), + priority: VSInternalPriorityLevel.Normal, + groupName: "Roslyn2", + applicableRange: new LSP.Range { Start = new Position { Line = 0, Character = 6 }, End = new Position { Line = 0, Character = 9 } }, + diagnostics: null); + + var testWorkspace = testLspServer.TestWorkspace; + var documentBefore = testWorkspace.CurrentSolution.GetDocument(testWorkspace.Documents.Single().Id); + var documentUriBefore = documentBefore.GetUriForRenamedDocument(); + + var actualResolvedAction = await RunGetCodeActionResolveAsync(testLspServer, unresolvedCodeAction); + + var documentAfter = testWorkspace.CurrentSolution.GetDocument(testWorkspace.Documents.Single().Id); + var documentUriAfter = documentBefore.WithName("ABC.cs").GetUriForRenamedDocument(); + + var expectedCodeAction = CodeActionsTests.CreateCodeAction( + title: string.Format(FeaturesResources.Rename_file_to_0, "ABC.cs"), + kind: CodeActionKind.Refactor, + children: Array.Empty(), + data: CreateCodeActionResolveData( + string.Format(FeaturesResources.Rename_file_to_0, "ABC.cs"), + testLspServer.GetLocations("caret").Single()), + priority: VSInternalPriorityLevel.Normal, + groupName: "Roslyn2", + applicableRange: new LSP.Range { Start = new Position { Line = 0, Character = 6 }, End = new Position { Line = 0, Character = 9 } }, + diagnostics: null, + edit: GenerateRenameFileEdit(new List<(Uri, Uri)> { (documentUriBefore, documentUriAfter) })); + + AssertJsonEquals(expectedCodeAction, actualResolvedAction); + } + + [WpfTheory, CombinatorialData] + public async Task TestLinkedDocuments(bool mutatingLspWorkspace) + { + var xmlWorkspace = @" + + + class C +{ + public static readonly int {|caret:_value|} = 10; +} + + + + + + +"; + await using var testLspServer = await CreateXmlTestLspServerAsync(xmlWorkspace, mutatingLspWorkspace); + + var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( + title: string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value"), + kind: CodeActionKind.Refactor, + children: Array.Empty(), + data: CreateCodeActionResolveData( + string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value"), + testLspServer.GetLocations("caret").Single()), + priority: VSInternalPriorityLevel.Normal, + groupName: "Roslyn2", + applicableRange: new LSP.Range { Start = new Position { Line = 2, Character = 33 }, End = new Position { Line = 39, Character = 2 } }, + diagnostics: null); + + var actualResolvedAction = await RunGetCodeActionResolveAsync(testLspServer, unresolvedCodeAction); + var edits = new TextEdit[] + { + new TextEdit() + { + NewText = "private", + Range = new LSP.Range() + { + Start = new Position + { + Line = 2, + Character = 4 + }, + End = new Position + { + Line = 2, + Character = 10 + } + } + }, + new TextEdit + { + NewText = string.Empty, + Range = new LSP.Range + { + Start = new Position + { + Line = 2, + Character = 31 + }, + End = new Position + { + Line = 2, + Character = 32 + } + } + }, + new TextEdit + { + NewText = @" + + public static int Value => value;", + Range = new LSP.Range + { + Start = new Position + { + Line = 2, + Character = 43 + }, + End = new Position + { + Line = 2, + Character = 43 + } + } + } + }; + var expectedCodeAction = CodeActionsTests.CreateCodeAction( + title: string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value"), + kind: CodeActionKind.Refactor, + children: Array.Empty(), + data: CreateCodeActionResolveData( + string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, "_value"), + testLspServer.GetLocations("caret").Single()), + priority: VSInternalPriorityLevel.Normal, + groupName: "Roslyn2", + applicableRange: new LSP.Range { Start = new Position { Line = 2, Character = 33 }, End = new Position { Line = 39, Character = 2 } }, + diagnostics: null, + edit: GenerateWorkspaceEdit(testLspServer.GetLocations("caret"), edits)); + AssertJsonEquals(expectedCodeAction, actualResolvedAction); + } + private static async Task RunGetCodeActionResolveAsync( TestLspServer testLspServer, VSInternalCodeAction unresolvedCodeAction) @@ -169,5 +330,12 @@ private static LSP.TextEdit GenerateTextEdit(string newText, LSP.Range range) } } }; + + private static WorkspaceEdit GenerateRenameFileEdit(IList<(Uri oldUri, Uri newUri)> renameLocations) + => new() + { + DocumentChanges = renameLocations.Select( + locations => new SumType(new RenameFile() { OldUri = locations.oldUri, NewUri = locations.newUri })).ToArray() + }; } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index 6f4fba6c1130b..30442ca82cd44 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -5,23 +5,14 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.AddImport; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Xunit; @@ -47,7 +38,7 @@ void M() {|caret:|}int i = 1; } }"; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, initializationOptions: new InitializationOptions() { ClientCapabilities = new VSInternalClientCapabilities { SupportsVisualStudioExtensions = true } }); var caretLocation = testLspServer.GetLocations("caret").Single(); var expected = CreateCodeAction( @@ -145,6 +136,76 @@ void M() Assert.Equal(AddImportDiagnosticIds.CS0103, addImport.Diagnostics.Single().Code.Value); } + [WpfTheory, CombinatorialData] + public async Task TestNoSuppressionFixerInStandardLSP(bool mutatingLspWorkspace) + { + var markup = @" +class ABC +{ + private static async void {|caret:XYZ|}() + { + } +}"; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var caret = testLspServer.GetLocations("caret").Single(); + var codeActionParams = new LSP.CodeActionParams + { + TextDocument = CreateTextDocumentIdentifier(caret.Uri), + Range = caret.Range, + Context = new LSP.CodeActionContext + { + Diagnostics = new[] + { + new LSP.Diagnostic + { + // async method lack of await. + Code = "CS1998" + } + } + } + }; + + var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); + Assert.Single(results); + Assert.Equal("Make method synchronous", results[0].Title); + } + + [WpfTheory, CombinatorialData] + public async Task TestStandardLspNestedCodeAction(bool mutatingLspWorkspace) + { + var markup = @" +class ABC +{ + private void XYZ() + { + var a = {|caret:A()|}; + } + + private int A() => 1; +}"; + + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var caret = testLspServer.GetLocations("caret").Single(); + var codeActionParams = new LSP.CodeActionParams + { + TextDocument = CreateTextDocumentIdentifier(caret.Uri), + Range = caret.Range, + Context = new LSP.CodeActionContext + { + } + }; + + var results = await RunGetCodeActionsAsync(testLspServer, codeActionParams); + var resultsTitles = results.Select(r => r.Title).ToArray(); + // Inline method refactoring provide nested code actions. + // Make sure it is correctly displayed. + Assert.True(resultsTitles.Contains("Inline 'A()' -> Inline 'A()'")); + Assert.True(resultsTitles.Contains("Inline 'A()' -> Inline and keep 'A()'")); + } + private static async Task RunGetCodeActionsAsync( TestLspServer testLspServer, CodeActionParams codeActionParams) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs index 0daa914e9cdba..847daa07110ba 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs @@ -5,7 +5,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; +using Newtonsoft.Json; using Roslyn.Test.Utilities; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -190,4 +193,54 @@ public async Task TestRecordDeclarationAsync(bool lspMutatingWorkspace) await using var testLspServer = await CreateTestLspServerAsync(markup, lspMutatingWorkspace, CapabilitiesWithVSExtensions); await VerifyCodeLensAsync(testLspServer, expectedNumberOfReferences: 0); } + + [Theory, CombinatorialData] + public async Task TestDoesNotShutdownServerIfCacheEntryMissing(bool mutatingLspWorkspace) + { + var markup = +@"class A +{ + void {|codeLens:M|}() + { + } + + void UseM() + { + M(); + } +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var textDocument = CreateTextDocumentIdentifier(testLspServer.GetCurrentSolution().Projects.Single().Documents.Single().GetURI()); + var codeLensParams = new LSP.CodeLensParams + { + TextDocument = textDocument + }; + + var actualCodeLenses = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParams, CancellationToken.None); + var firstCodeLens = actualCodeLenses.First(); + var data = JsonConvert.DeserializeObject(firstCodeLens.Data!.ToString()); + AssertEx.NotNull(data); + var firstResultId = data.ResultId; + + // Verify the code lens item is in the cache. + var cache = testLspServer.GetRequiredLspService(); + Assert.NotNull(cache.GetCachedEntry(firstResultId)); + + // Execute a few more requests to ensure the first request is removed from the cache. + await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParams, CancellationToken.None); + await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParams, CancellationToken.None); + var lastCodeLenses = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParams, CancellationToken.None); + Assert.True(lastCodeLenses.Any()); + + // Assert that the first result id is no longer in the cache. + Assert.Null(cache.GetCachedEntry(firstResultId)); + + // Assert that the request throws because the item no longer exists in the cache. + await Assert.ThrowsAsync(async () => await testLspServer.ExecuteRequestAsync(LSP.Methods.CodeLensResolveName, firstCodeLens, CancellationToken.None)); + + // Assert that the server did not shutdown and that we can resolve the latest codelens request we made. + var lastCodeLens = await testLspServer.ExecuteRequestAsync(LSP.Methods.CodeLensResolveName, lastCodeLenses.First(), CancellationToken.None); + Assert.NotNull(lastCodeLens?.Command); + } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs index 2a5c92a9dbc11..7cf182cdc2927 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -182,7 +183,7 @@ class B : A var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var selectedItem = CodeAnalysis.Completion.CompletionItem.Create(displayText: "M"); - var textEdit = await CompletionResolveHandler.GenerateTextEditAsync( + var textEdit = await EditorLspCompletionResultCreationService.GenerateTextEditAsync( document, new TestCaretOutOfScopeCompletionService(testLspServer.TestWorkspace.Services.SolutionServices), selectedItem, snippetsSupported: true, CancellationToken.None).ConfigureAwait(false); Assert.Equal(@"public override void M() diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs index 8839d76ab9e6b..4a21b8f3c86d3 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs @@ -493,13 +493,13 @@ void M() var defaultRange = new LSP.Range { - Start = new LSP.Position { Line = 5, Character = 20 }, + Start = new LSP.Position { Line = 5, Character = 21 }, End = new LSP.Position { Line = 5, Character = 21 } }; var expected = await CreateCompletionItemAsync( label: @"\A", kind: LSP.CompletionItemKind.Text, tags: new string[] { "Text" }, request: completionParams, document: document, - sortText: "0000").ConfigureAwait(false); + sortText: "0000", vsResolveTextEditOnCommit: true).ConfigureAwait(false); var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); @@ -530,13 +530,13 @@ void M() var defaultRange = new LSP.Range { - Start = new LSP.Position { Line = 5, Character = 23 }, + Start = new LSP.Position { Line = 5, Character = 25 }, End = new LSP.Position { Line = 5, Character = 25 } }; var expected = await CreateCompletionItemAsync( - label: @"\A", kind: LSP.CompletionItemKind.Text, tags: new string[] { "Text" }, request: completionParams, document: document, insertText: @"\\A", - sortText: "0000").ConfigureAwait(false); + label: @"\A", kind: LSP.CompletionItemKind.Text, tags: new string[] { "Text" }, request: completionParams, document: document, + sortText: "0000", vsResolveTextEditOnCommit: true).ConfigureAwait(false); var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index 3093ea047200d..345c243e74a9b 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -6,10 +6,17 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.InlineHints; +using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; +using Microsoft.CodeAnalysis.Text; +using Newtonsoft.Json; +using Roslyn.Test.Utilities; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; +using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.InlayHint { @@ -99,6 +106,58 @@ void X((int, bool) d) await RunVerifyInlayHintAsync(markup, mutatingLspWorkspace, hasTextEdits: false); } + [Theory, CombinatorialData] + public async Task TestDoesNotShutdownServerIfCacheEntryMissing(bool mutatingLspWorkspace) + { + var markup = +@"class A +{ + void M() + { + var {|int:|}x = 5; + } +}"; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.CSharp, true); + testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption(InlineHintsOptionsStorage.EnabledForTypes, LanguageNames.CSharp, true); + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + var textDocument = CreateTextDocumentIdentifier(document.GetURI()); + var sourceText = await document.GetTextAsync(); + var span = TextSpan.FromBounds(0, sourceText.Length); + + var inlayHintParams = new LSP.InlayHintParams + { + TextDocument = textDocument, + Range = ProtocolConversions.TextSpanToRange(span, sourceText) + }; + + var actualInlayHints = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); + var firstInlayHint = actualInlayHints.First(); + var data = JsonConvert.DeserializeObject(firstInlayHint.Data!.ToString()); + AssertEx.NotNull(data); + var firstResultId = data.ResultId; + + // Verify the inlay hint item is in the cache. + var cache = testLspServer.GetRequiredLspService(); + Assert.NotNull(cache.GetCachedEntry(firstResultId)); + + // Execute a few more requests to ensure the first request is removed from the cache. + await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); + await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); + var lastInlayHints = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); + Assert.True(lastInlayHints.Any()); + + // Assert that the first result id is no longer in the cache. + Assert.Null(cache.GetCachedEntry(firstResultId)); + + // Assert that the request throws because the item no longer exists in the cache. + await Assert.ThrowsAsync(async () => await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, firstInlayHint, CancellationToken.None)); + + // Assert that the server did not shutdown and that we can resolve the latest inlay hint request we made. + var lastInlayHint = await testLspServer.ExecuteRequestAsync(LSP.Methods.InlayHintResolveName, lastInlayHints.First(), CancellationToken.None); + Assert.NotNull(lastInlayHint?.ToolTip); + } + private async Task RunVerifyInlayHintAsync(string markup, bool mutatingLspWorkspace, bool hasTextEdits = true) { await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, CapabilitiesWithVSExtensions); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs index 32da30e0a42c4..32416e735c7c9 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs @@ -71,12 +71,20 @@ void M() await testLspServer.OpenDocumentAsync(looseFileUri, string.Empty).ConfigureAwait(false); await AssertFileInMiscWorkspaceAsync(testLspServer, looseFileUri).ConfigureAwait(false); + // Assert that the misc workspace contains the initial document. + var miscWorkspaceText = await GetMiscellaneousDocument(testLspServer)!.GetTextAsync(CancellationToken.None); + Assert.Empty(miscWorkspaceText.ToString()); + // Make a text change to the loose file and verify requests appropriately reflect the changes. await testLspServer.InsertTextAsync(looseFileUri, (0, 0, source)).ConfigureAwait(false); var caret = new LSP.Location { Range = new() { Start = new(0, 6), End = new(0, 7) }, Uri = looseFileUri }; var hover = await RunGetHoverAsync(testLspServer, caret).ConfigureAwait(false); Assert.Contains("class A", hover.Contents!.Value.Fourth.Value); await AssertFileInMiscWorkspaceAsync(testLspServer, looseFileUri).ConfigureAwait(false); + + // Assert that the misc workspace contains the updated document. + miscWorkspaceText = await GetMiscellaneousDocument(testLspServer)!.GetTextAsync(CancellationToken.None); + Assert.Contains("class A", miscWorkspaceText.ToString()); } [Theory, CombinatorialData] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs index 3fc9848799d03..8a3e3b82ac7d5 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs @@ -32,7 +32,7 @@ public RequestOrderingTests(ITestOutputHelper testOutputHelper) : base(testOutpu .AddParts(typeof(LongRunningNonMutatingRequestHandler)); [Theory, CombinatorialData] - public async Task MutatingRequestsDontOverlap(bool mutatingLspWorkspace) + public async Task MutatingRequestsDoNotOverlap(bool mutatingLspWorkspace) { var requests = new[] { new TestRequest(MutatingRequestHandler.MethodName), diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs index 55f5ee133837b..2143858418bc8 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs @@ -4,6 +4,7 @@ #nullable enable +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -11,6 +12,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -25,6 +27,9 @@ protected AbstractSemanticTokensTests(ITestOutputHelper testOutputHelper) : base { } + private protected static IReadOnlyDictionary GetTokenTypeToIndex(TestLspServer server) + => SemanticTokensSchema.GetSchema(server.ClientCapabilities).TokenTypeToIndex; + private protected static async Task RunGetSemanticTokensRangeAsync(TestLspServer testLspServer, LSP.Location caret, LSP.Range range) { var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentSemanticTokensRangeName, @@ -88,7 +93,8 @@ private protected static async Task VerifyBasicInvariantsAndNoMultiLineTokens(Te var lineLength = text.Lines[currentLine].SpanIncludingLineBreak.Length; // If this assertion fails, we didn't break up a multi-line token properly. - var kind = SemanticTokensHelpers.TokenTypeToIndex.Where(kvp => kvp.Value == tokens[i + 3]).Single().Key; + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + var kind = tokenTypeToIndex.Where(kvp => kvp.Value == tokens[i + 3]).Single().Key; Assert.True(currentChar + tokenLength <= lineLength, $"Multi-line token of type {kind} found on line {currentLine} at character index {currentChar}. " + @@ -101,13 +107,15 @@ private protected static async Task VerifyBasicInvariantsAndNoMultiLineTokens(Te /// fail. This groups rows by five (so that way the diff can't desynced from the start of a new token), and also replaces the token index /// back with the string again. /// - protected static ImmutableArray ConvertToReadableFormat(int[] data) + protected static ImmutableArray ConvertToReadableFormat( + ClientCapabilities capabilities, int[] data) { var convertedStringsBuilder = ImmutableArray.CreateBuilder(data.Length / 5); + var tokenTypeToIndex = SemanticTokensSchema.GetSchema(capabilities).TokenTypeToIndex; for (var i = 0; i < data.Length; i += 5) { - var kind = SemanticTokensHelpers.TokenTypeToIndex.Single(kvp => kvp.Value == data[i + 3]).Key; + var kind = tokenTypeToIndex.Single(kvp => kvp.Value == data[i + 3]).Key; convertedStringsBuilder.Add($"{data[i]}, {data[i + 1]}, {data[i + 2]}, {kind}, {data[i + 4]}"); } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs index 9820ca9245ce2..6cd2215301e52 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs @@ -27,96 +27,101 @@ public SemanticTokensRangeTests(ITestOutputHelper testOutputHelper) : base(testO } [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_FullDocAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_FullDocAsync(bool mutatingLspWorkspace, bool isVS) { var markup = @"{|caret:|}// Comment static class C { } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) }; var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range); - // Everything is colorized syntactically, so we shouldn't be returning any semantic results. - var expectedResults = new LSP.SemanticTokens + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) { - Data = Array.Empty() - }; - - Assert.Equal(expectedResults.Data, results.Data); - } - - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_FullDoc_RazorAsync(bool mutatingLspWorkspace) - { - // Razor docs should be returning semantic + syntactic reuslts. - var markup = -@"{|caret:|}// Comment -static class C { } -"; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - var range = new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) }; - var options = ClassificationOptions.Default; - - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications: true, CancellationToken.None); - - var expectedResults = new LSP.SemanticTokens + expectedResults.Data = new int[] + { + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } + else { - Data = new int[] + expectedResults.Data = new int[] { // Line | Char | Len | Token type | Modifier - 0, 0, 10, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0, // '// Comment' - 1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - }, - }; - - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results)); + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } + + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); } [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_PartialDoc_RazorAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_PartialDocAsync(bool mutatingLspWorkspace, bool isVS) { // Razor docs should be returning semantic + syntactic reuslts. var markup = @"{|caret:|}// Comment static class C { } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(1, 0), End = new Position(2, 0) }; var options = ClassificationOptions.Default; var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications: true, CancellationToken.None); + testLspServer.ClientCapabilities, document, range, options, CancellationToken.None); - var expectedResults = new LSP.SemanticTokens + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = new int[] + { + // Line | Char | Len | Token type | Modifier + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } + else { - Data = new int[] + expectedResults.Data = new int[] { // Line | Char | Len | Token type | Modifier - 1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - }, - }; + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results)); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_MultiLineComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_MultiLineComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) { // Testing as a Razor doc so we get both syntactic + semantic results; otherwise the results would be empty. var markup = @@ -125,68 +130,52 @@ public async Task TestGetSemanticTokensRange_MultiLineComment_IncludeSyntacticCl two three */ } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(4, 0) }; var options = ClassificationOptions.Default; var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications: true, CancellationToken.None); + testLspServer.ClientCapabilities, document, range, options, CancellationToken.None); - var expectedResults = new LSP.SemanticTokens + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) { - Data = new int[] + expectedResults.Data = new int[] { // Line | Char | Len | Token type | Modifier - 0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 6, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0, // '/* one' - 2, 0, 3, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0, // 'two' - 1, 0, 8, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Comment], 0, // 'three */' - 0, 9, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - }, - }; - - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results)); - } - - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_StringLiteralAsync(bool mutatingLspWorkspace) - { - var markup = -@"{|caret:|}class C -{ - void M() - { - var x = @""one -two """" -three""; - } -} -"; - - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); - var range = new LSP.Range { Start = new Position(0, 0), End = new Position(9, 0) }; - var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range); - - var expectedResults = new LSP.SemanticTokens + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 6, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '/* one' + 2, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'two' + 1, 0, 8, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'three */' + 0, 9, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } + else { - Data = new int[] + expectedResults.Data = new int[] { - // Line | Char | Len | Token type | Modifier - 4, 8, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 1, 4, 2, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0, // '""' - }, - }; + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 6, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '/* one' + 2, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'two' + 1, 0, 8, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'three */' + 0, 9, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data!).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results.Data)); + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_StringLiteral_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_StringLiteral_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) { var markup = @"{|caret:|}class C @@ -200,46 +189,74 @@ void M() } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(9, 0) }; var options = ClassificationOptions.Default; var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications: true, CancellationToken.None); + testLspServer.ClientCapabilities, document, range, options, CancellationToken.None); - var expectedResults = new LSP.SemanticTokens + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = new int[] + { + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 4, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 8, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 5, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"one' + 1, 0, 4, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'two ' + 0, 4, 2, tokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0, // '""' + 1, 0, 6, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'three"' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } + else { - Data = new int[] + expectedResults.Data = new int[] { // Line | Char | Len | Token type | Modifier - 0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 4, 4, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 8, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 5, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"one' - 1, 0, 4, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'two ' - 0, 4, 2, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0, // '""' - 1, 0, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'three"' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - 1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - }, - }; + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 4, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 8, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 5, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"one' + 1, 0, 4, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'two ' + 0, 4, 2, tokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0, // '""' + 1, 0, 6, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'three"' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + }; + } await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results)); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_Regex_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_Regex_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) { var markup = @"{|caret:|}using System.Text.RegularExpressions; @@ -253,60 +270,101 @@ void M() } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var range = new LSP.Range { Start = new Position(0, 0), End = new Position(9, 0) }; var options = ClassificationOptions.Default; var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, SemanticTokensHelpers.TokenTypeToIndex, range, options, includeSyntacticClassifications: true, CancellationToken.None); + testLspServer.ClientCapabilities, document, range, options, CancellationToken.None); - var expectedResults = new LSP.SemanticTokens + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = new int[] + { + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + }; + } + else { - Data = new int[] + expectedResults.Data = new int[] { // Line | Char | Len | Token type | Modifier - 0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'using' - 0, 6, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 4, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' - 0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 18, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' - 0, 18, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 2, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 1, 4, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 1, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 2, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 3, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'new' - 0, 4, 5, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' - 0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.String], 0, // '"' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' - 0, 1, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' - 0, 3, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.String], 0, // '"' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - 1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - } - }; + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + }; + } await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results)); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } [Theory, CombinatorialData] [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1710519")] - public async Task TestGetSemanticTokensRange_RegexWithComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_RegexWithComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) { var markup = @"{|caret:|}using System.Text.RegularExpressions; @@ -321,69 +379,123 @@ void M() } "; - await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var options = ClassificationOptions.Default; var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, SemanticTokensHelpers.TokenTypeToIndex, range: null, options, includeSyntacticClassifications: true, CancellationToken.None); + testLspServer.ClientCapabilities, document, range: null, options: options, cancellationToken: CancellationToken.None); - var expectedResults = new LSP.SemanticTokens + var expectedResults = new LSP.SemanticTokens(); + + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = new int[] + { + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 2, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // ' ' + 0, 1, 9, tokenTypeToIndex[ClassificationTypeNames.RegexComment], 0, // '#comment' + 1, 0, 27, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '"' + 0, 27, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ',' + 0, 2, 12, tokenTypeToIndex[ClassificationTypeNames.EnumName], 0, // 'RegexOptions' + 0, 12, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 23, tokenTypeToIndex[ClassificationTypeNames.EnumMemberName], 0, // 'IgnorePatternWhitespace' + 0, 23, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + }; + } + else { - Data = new int[] + expectedResults.Data = new int[] { // Line | Char | Len | Token type | Modifier - 0, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'using' - 0, 6, 6, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 4, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' - 0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 18, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' - 0, 18, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 2, 0, 5, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 1, 4, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 1, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 2, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 3, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Keyword], 0, // 'new' - 0, 4, 5, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' - 0, 5, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 2, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"' - 0, 2, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' - 0, 1, 3, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' - 0, 3, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // ' ' - 0, 1, 9, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.RegexComment], 0, // '#comment' - 1, 0, 27, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '"' - 0, 27, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ',' - 0, 2, 12, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.EnumName], 0, // 'RegexOptions' - 0, 12, 1, SemanticTokensHelpers.TokenTypeToIndex[LSP.SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 23, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.EnumMemberName], 0, // 'IgnorePatternWhitespace' - 0, 23, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 0, 1, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - 1, 0, 1, SemanticTokensHelpers.TokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - } - }; + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 2, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // ' ' + 0, 1, 9, tokenTypeToIndex[ClassificationTypeNames.RegexComment], 0, // '#comment' + 1, 0, 27, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '"' + 0, 27, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ',' + 0, 2, 12, tokenTypeToIndex[SemanticTokenTypes.Enum], 0, // 'RegexOptions' + 0, 12, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 23, tokenTypeToIndex[SemanticTokenTypes.EnumMember], 0, // 'IgnorePatternWhitespace' + 0, 23, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + }; + } await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(expectedResults.Data), ConvertToReadableFormat(results)); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } - [Theory, MemberData(nameof(ClassificationTypeNamesToMatch))] - public void TestGetSemanticTokensRange_AssertCustomTokenTypes(string fieldName) - => Assert.True(SemanticTokensHelpers.RoslynCustomTokenTypes.Contains(fieldName), $"Missing token type {fieldName}."); + [Theory, CombinatorialData] + public void TestGetSemanticTokensRange_AssertCustomTokenTypes(bool isVS) + { + var capabilities = GetCapabilities(isVS); + var schema = SemanticTokensSchema.GetSchema(capabilities); + var tokenTypeMap = schema.TokenTypeMap; + var classificationTypeNamesToMatch = ClassificationTypeNames.AllTypeNames.Where( + type => !tokenTypeMap.ContainsKey(type) && !ClassificationTypeNames.AdditiveTypeNames.Contains(type)); - public static IEnumerable ClassificationTypeNamesToMatch => ClassificationTypeNames.AllTypeNames.Where( - type => !SemanticTokensHelpers.ClassificationTypeToSemanticTokenTypeMap.ContainsKey(type) && - !ClassificationTypeNames.AdditiveTypeNames.Contains(type)).Select(field => new object[] { field }); + var tokenTypes = schema.CustomTokenTypes; + foreach (var fieldName in classificationTypeNamesToMatch) + Assert.True(tokenTypes.Contains(fieldName), $"Missing token type {fieldName}."); + } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs index a85ff846182d8..8b829521dc51f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs @@ -221,7 +221,7 @@ void M() } [Theory, CombinatorialData] - public async Task DontShrinkValidMultilineBreakpoints(bool mutatingLspWorkspace) + public async Task DoNotShrinkValidMultilineBreakpoints(bool mutatingLspWorkspace) { var markup = @"class A diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 0c6033555dfc5..b0857f4633d0d 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -5,16 +5,11 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Graph; @@ -22,7 +17,6 @@ using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LspProtocol = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -71,16 +65,14 @@ public static Generator CreateAndWriteCapabilitiesVertex(ILsifJsonWriter lsifJso { var generator = new Generator(lsifJsonWriter, logFile); - // Pass the set of supported SemanticTokenTypes. Order must match - // the order used for serialization of semantic tokens array. This - // array is analogous to the equivalent array in https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_semanticTokens. + // Pass the set of supported SemanticTokenTypes. Order must match the order used for serialization of + // semantic tokens array. This array is analogous to the equivalent array in + // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_semanticTokens. // - // Ideally semantic tokens support would use the well-known, common - // set of token types specified in LSP's SemanticTokenTypes to reduce - // the number of tokens a particular LSIF consumer must understand, - // but Roslyn currently employs a large number of custom token types - // that aren't yet standardized in LSP or LSIF's well-known set so we - // will pass both LSP and Roslyn custom token types for now. + // Ideally semantic tokens support would use the well-known, common set of token types specified in LSP's + // SemanticTokenTypes to reduce the number of tokens a particular LSIF consumer must understand, but Roslyn + // currently employs a large number of custom token types that aren't yet standardized in LSP or LSIF's + // well-known set so we will pass both LSP and Roslyn custom token types for now. var capabilitiesVertex = new Capabilities( generator._idFactory, HoverProvider, @@ -91,7 +83,7 @@ public static Generator CreateAndWriteCapabilitiesVertex(ILsifJsonWriter lsifJso DocumentSymbolProvider, FoldingRangeProvider, DiagnosticProvider, - new SemanticTokensCapabilities(SemanticTokensHelpers.AllTokenTypes, new[] { SemanticTokenModifiers.Static })); + new SemanticTokensCapabilities(SemanticTokensSchema.LegacyTokensSchemaForLSIF.AllTokenTypes, new[] { SemanticTokenModifiers.Static })); generator._lsifJsonWriter.Write(capabilitiesVertex); return generator; } @@ -437,17 +429,17 @@ void MarkImplementationOfSymbol(ISymbol baseMember) { // Compute colorization data. // - // Unlike the mainline LSP scenario, where we control both the syntatic colorizer (in-proc syntax tagger) - // and the semantic colorizer (LSP semantic tokens) LSIF is more likely to be consumed by clients - // which may have different syntatic classification behavior than us, resulting in missing colors. To avoid - // this, we include syntax tokens in the generated data. + // Unlike the mainline LSP scenario, where we control both the syntactic colorizer (in-proc syntax tagger) + // and the semantic colorizer (LSP semantic tokens) LSIF is more likely to be consumed by clients which may + // have different syntactic classification behavior than us, resulting in missing colors. To avoid this, we + // include syntax tokens in the generated data. var data = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + // Just get the pure-lsp semantic tokens here. + new VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }, document, - SemanticTokensHelpers.TokenTypeToIndex, range: null, - Classification.ClassificationOptions.Default, - includeSyntacticClassifications: true, - CancellationToken.None); + options: Classification.ClassificationOptions.Default, + cancellationToken: CancellationToken.None); var semanticTokensResult = new SemanticTokensResult(new SemanticTokens { Data = data }, idFactory); var semanticTokensEdge = Edge.Create(Methods.TextDocumentSemanticTokensFullName, documentVertex.GetId(), semanticTokensResult.GetId(), idFactory); diff --git a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj index 520db11a5f594..4c1e44e180bff 100644 --- a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj +++ b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj @@ -45,6 +45,7 @@ Condition="'%(ReferenceCopyLocalPaths.DestinationSubDirectory)' == '' and '%(Extension)' != '.pdb' and '%(Extension)' != '.xml'" /> + diff --git a/src/Features/Lsif/Generator/Program.cs b/src/Features/Lsif/Generator/Program.cs index 4425eb9214599..278da8f45f41c 100644 --- a/src/Features/Lsif/Generator/Program.cs +++ b/src/Features/Lsif/Generator/Program.cs @@ -105,7 +105,12 @@ public static Task Main(string[] args) else { Contract.ThrowIfNull(binLog); - await LocateAndRegisterMSBuild(logFile, binLog.Directory); + + // If we're loading a binlog, we don't need to discover an MSBuild that matches the SDK or source that we're processing, since we're not running + // any MSBuild builds or tasks/targets in our process. Since we're reading a binlog, simply none of the SDK will be loaded. We might load analyzers + // or source generators from the SDK or user-built, but those must generally target netstandard2.0 so we don't really expect them to have problems loading + // on one version of the runtime versus another. + await LocateAndRegisterMSBuild(logFile, sourceDirectory: null); await GenerateFromBinaryLogAsync(binLog, lsifWriter, logFile, cancellationToken); } } @@ -122,13 +127,13 @@ public static Task Main(string[] args) await logFile.WriteLineAsync("Generation complete."); } - private static async Task LocateAndRegisterMSBuild(TextWriter logFile, DirectoryInfo? workingDirectory) + private static async Task LocateAndRegisterMSBuild(TextWriter logFile, DirectoryInfo? sourceDirectory) { // Make sure we pick the highest version var options = VisualStudioInstanceQueryOptions.Default; - if (workingDirectory != null) - options.WorkingDirectory = workingDirectory.FullName; + if (sourceDirectory != null) + options.WorkingDirectory = sourceDirectory.FullName; var msBuildInstance = MSBuildLocator.QueryVisualStudioInstances(options).OrderByDescending(i => i.Version).FirstOrDefault(); if (msBuildInstance == null) diff --git a/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb b/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb index 1cd6e6b528aff..c41fb236b127d 100644 --- a/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb +++ b/src/Features/Lsif/GeneratorTest/RangeResultSetTests.vb @@ -58,7 +58,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests ) - Assert.Empty(lsif.Vertices.OfType(Of Range)) + Assert.Empty(lsif.Vertices.OfType(Of Graph.Range)) End Function diff --git a/src/Features/Test/Microsoft.CodeAnalysis.Features.UnitTests.csproj b/src/Features/Test/Microsoft.CodeAnalysis.Features.UnitTests.csproj new file mode 100644 index 0000000000000..e0253a29743dc --- /dev/null +++ b/src/Features/Test/Microsoft.CodeAnalysis.Features.UnitTests.csproj @@ -0,0 +1,52 @@ + + + + + Library + Microsoft.CodeAnalysis.Editor.UnitTests + net472 + true + true + + + + + + + + + + + + + + + global,WORKSPACES + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj b/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj new file mode 100644 index 0000000000000..002cd493e6d3a --- /dev/null +++ b/src/Features/TestUtilities/Microsoft.CodeAnalysis.Features.Test.Utilities.csproj @@ -0,0 +1,108 @@ + + + + + Library + Microsoft.CodeAnalysis.Test.Utilities + net472 + true + true + false + + + + + + + + + + + global,WORKSPACES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb index d3ddc110d21fa..94694f171c585 100644 --- a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb +++ b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb @@ -754,21 +754,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature End Get End Property - Protected Overrides Function CreateExplicitParamsArrayFromIndividualArguments(newArguments As SeparatedSyntaxList(Of SyntaxNode), indexInExistingList As Integer, parameterSymbol As IParameterSymbol) As SyntaxNode + Protected Overrides Function CreateExplicitParamsArrayFromIndividualArguments(Of TArgumentSyntax As SyntaxNode)(newArguments As SeparatedSyntaxList(Of TArgumentSyntax), indexInExistingList As Integer, parameterSymbol As IParameterSymbol) As TArgumentSyntax ' A params array cannot be introduced due to the addition of an omitted ' argument in VB because you cannot have a named argument to a params array. Throw New InvalidOperationException() End Function - Protected Overrides Function AddNameToArgument(newArgument As SyntaxNode, name As String) As SyntaxNode + Protected Overrides Function AddNameToArgument(Of TArgumentSyntax As SyntaxNode)(newArgument As TArgumentSyntax, name As String) As TArgumentSyntax Dim simpleArgument = TryCast(newArgument, SimpleArgumentSyntax) If simpleArgument IsNot Nothing Then - Return simpleArgument.WithNameColonEquals(NameColonEquals(IdentifierName(name))) + Return CType(CType(simpleArgument.WithNameColonEquals(NameColonEquals(IdentifierName(name))), SyntaxNode), TArgumentSyntax) End If Dim omittedArgument = TryCast(newArgument, OmittedArgumentSyntax) If omittedArgument IsNot Nothing Then - Return omittedArgument + Return CType(CType(omittedArgument, SyntaxNode), TArgumentSyntax) End If Throw ExceptionUtilities.UnexpectedValue(newArgument.Kind()) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb index dd514063e469f..b1a1d7f716ec6 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ExtensionMethodImportCompletionProvider.vb @@ -8,7 +8,6 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.Host.Mef -Imports Microsoft.CodeAnalysis.[Shared].Extensions.ContextQuery Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers @@ -37,14 +36,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Property Public Overrides Function IsInsertionTrigger(text As SourceText, characterPosition As Integer, options As CompletionOptions) As Boolean - Return CompletionUtilities.IsDefaultTriggerCharacterOrParen(text, characterPosition, options) + Return IsDefaultTriggerCharacterOrParen(text, characterPosition, options) End Function - Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = CompletionUtilities.CommonTriggerCharsAndParen - - Protected Overrides Function GetImportedNamespacesAsync(syntaxContext As SyntaxContext, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of String)) - Return ImportCompletionProviderHelper.GetImportedNamespacesAsync(syntaxContext, cancellationToken) - End Function + Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = CommonTriggerCharsAndParen Protected Overrides Function IsFinalSemicolonOfUsingOrExtern(directive As SyntaxNode, token As SyntaxToken) As Boolean Return False diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb deleted file mode 100644 index 948719ea13641..0000000000000 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/ImportCompletionProviderHelper.vb +++ /dev/null @@ -1,37 +0,0 @@ -' Licensed to the .NET Foundation under one or more agreements. -' The .NET Foundation licenses this file to you under the MIT license. -' See the LICENSE file in the project root for more information. - -Imports System.Collections.Immutable -Imports System.Threading -Imports Microsoft.CodeAnalysis.PooledObjects -Imports Microsoft.CodeAnalysis.[Shared].Extensions.ContextQuery - -Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers - - Friend NotInheritable Class ImportCompletionProviderHelper - - Public Shared Async Function GetImportedNamespacesAsync(syntaxContext As SyntaxContext, token As CancellationToken) As Task(Of ImmutableArray(Of String)) - - ' The location Is the containing node of the LeftToken, Or the compilation unit itself if LeftToken - ' indicates the beginning of the document (i.e. no parent). - Dim Location = If(syntaxContext.LeftToken.Parent, Await syntaxContext.SyntaxTree.GetRootAsync(token).ConfigureAwait(False)) - - Dim builder = ArrayBuilder(Of String).GetInstance() - - ' Get namespaces from import directives - Dim importsInScope = syntaxContext.SemanticModel.GetImportNamespacesInScope(Location) - For Each import As INamespaceSymbol In importsInScope - builder.Add(import.ToDisplayString(SymbolDisplayFormats.NameFormat)) - Next - - ' Get global imports from compilation option - Dim vbOptions = DirectCast(syntaxContext.SemanticModel.Compilation.Options, VisualBasicCompilationOptions) - For Each globalImport As GlobalImport In vbOptions.GlobalImports - builder.Add(globalImport.Name) - Next - - Return builder.ToImmutableAndFree() - End Function - End Class -End Namespace diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb index 6d0ac1e851db5..00bc58aa2c6b1 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImportCompletionProvider/TypeImportCompletionProvider.vb @@ -8,7 +8,6 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.Host.Mef -Imports Microsoft.CodeAnalysis.[Shared].Extensions.ContextQuery Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -32,14 +31,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Property Public Overrides Function IsInsertionTrigger(text As SourceText, characterPosition As Integer, options As CompletionOptions) As Boolean - Return CompletionUtilities.IsDefaultTriggerCharacterOrParen(text, characterPosition, options) + Return IsDefaultTriggerCharacterOrParen(text, characterPosition, options) End Function - Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = CompletionUtilities.CommonTriggerCharsAndParen - - Protected Overrides Function GetImportedNamespacesAsync(syntaxContext As SyntaxContext, cancellationToken As CancellationToken) As Task(Of ImmutableArray(Of String)) - Return ImportCompletionProviderHelper.GetImportedNamespacesAsync(syntaxContext, cancellationToken) - End Function + Public Overrides ReadOnly Property TriggerCharacters As ImmutableHashSet(Of Char) = CommonTriggerCharsAndParen Protected Overrides Function IsFinalSemicolonOfUsingOrExtern(directive As SyntaxNode, token As SyntaxToken) As Boolean Return False diff --git a/src/Features/VisualBasic/Portable/ConvertForEachToFor/VisualBasicConvertForEachToForCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/ConvertForEachToFor/VisualBasicConvertForEachToForCodeRefactoringProvider.vb index b1841a464dfb1..ff196d5b3ec93 100644 --- a/src/Features/VisualBasic/Portable/ConvertForEachToFor/VisualBasicConvertForEachToForCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/ConvertForEachToFor/VisualBasicConvertForEachToForCodeRefactoringProvider.vb @@ -79,11 +79,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ConvertForEachToFor If nextStatement.ControlVariables.Count > 0 Then Debug.Assert(nextStatement.ControlVariables.Count = 1) - Dim controlVariable As SyntaxNode = nextStatement.ControlVariables(0) - controlVariable = generator.IdentifierName( + Dim controlVariable As ExpressionSyntax = nextStatement.ControlVariables(0) + controlVariable = CType(generator.IdentifierName( indexVariable _ .WithLeadingTrivia(controlVariable.GetFirstToken().LeadingTrivia) _ - .WithTrailingTrivia(controlVariable.GetLastToken().TrailingTrivia)) + .WithTrailingTrivia(controlVariable.GetLastToken().TrailingTrivia)), ExpressionSyntax) nextStatement = nextStatement.WithControlVariables( SyntaxFactory.SingletonSeparatedList(controlVariable)) diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index bd80f56145a0b..b5adb50c35fb1 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -673,7 +673,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return comparer.ComputeMatch(oldDeclaration.Parent, newDeclaration.Parent) End Function - Protected Overrides Function ComputeBodyMatch(oldBody As SyntaxNode, newBody As SyntaxNode, knownMatches As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode))) As Match(Of SyntaxNode) + Protected Overrides Function ComputeBodyMatchImpl(oldBody As SyntaxNode, newBody As SyntaxNode, knownMatches As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode))) As Match(Of SyntaxNode) SyntaxUtilities.AssertIsBody(oldBody, allowLambda:=True) SyntaxUtilities.AssertIsBody(newBody, allowLambda:=True) @@ -979,8 +979,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If End Function - Protected Overrides Function AreEquivalent(left As SyntaxNode, right As SyntaxNode) As Boolean - Return SyntaxFactory.AreEquivalent(left, right) + Protected Overrides Function AreEquivalentLambdaBodies(oldLambda As SyntaxNode, oldLambdaBody As SyntaxNode, newLambda As SyntaxNode, newLambdaBody As SyntaxNode) As Boolean + Dim oldBodyTopMostNodes = LambdaUtilities.GetLambdaBodyExpressionsAndStatements(oldLambdaBody) + Dim newBodyTopMostNodes = LambdaUtilities.GetLambdaBodyExpressionsAndStatements(newLambdaBody) + + Return oldBodyTopMostNodes.SequenceEqual(newBodyTopMostNodes, AddressOf SyntaxFactory.AreEquivalent) End Function Private Shared Function AreEquivalentIgnoringLambdaBodies(left As SyntaxNode, right As SyntaxNode) As Boolean @@ -1415,6 +1418,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return False End Function + Friend Overrides Function IsGenericLocalFunction(node As SyntaxNode) As Boolean + Return False + End Function + Friend Overrides Function TryGetLambdaBodies(node As SyntaxNode, ByRef body1 As SyntaxNode, ByRef body2 As SyntaxNode) As Boolean Return LambdaUtilities.TryGetLambdaBodies(node, body1, body2) End Function @@ -2375,22 +2382,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Select End Sub - Public Sub ClassifyDeclarationBodyRudeUpdates(newDeclarationOrBody As SyntaxNode) - For Each node In newDeclarationOrBody.DescendantNodesAndSelf() - Select Case node.Kind - Case SyntaxKind.AggregateClause, + Public Sub ClassifyMemberOrLambdaBodyRudeUpdates(newBody As SyntaxNode) + For Each topMostBodyNode In If(LambdaUtilities.IsLambdaBody(newBody), LambdaUtilities.GetLambdaBodyExpressionsAndStatements(newBody), {newBody}) + For Each node In topMostBodyNode.DescendantNodesAndSelf(AddressOf LambdaUtilities.IsNotLambda) + Select Case node.Kind() + Case SyntaxKind.AggregateClause, SyntaxKind.GroupByClause, SyntaxKind.SimpleJoinClause, SyntaxKind.GroupJoinClause - ReportError(RudeEditKind.ComplexQueryExpression, node, Me._newNode) - Return - - Case SyntaxKind.LocalDeclarationStatement - Dim declaration = DirectCast(node, LocalDeclarationStatementSyntax) - If declaration.Modifiers.Any(SyntaxKind.StaticKeyword) Then - ReportError(RudeEditKind.UpdateStaticLocal) - End If - End Select + ReportError(RudeEditKind.ComplexQueryExpression, node, Me._newNode) + Return + + Case SyntaxKind.LocalDeclarationStatement + Dim declaration = DirectCast(node, LocalDeclarationStatementSyntax) + If declaration.Modifiers.Any(SyntaxKind.StaticKeyword) Then + ReportError(RudeEditKind.UpdateStaticLocal) + End If + End Select + Next Next End Sub #End Region @@ -2431,9 +2440,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue classifier.ClassifyEdit() End Sub - Friend Overrides Sub ReportMemberBodyUpdateRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), newMember As SyntaxNode, span As TextSpan?) - Dim classifier = New EditClassifier(Me, diagnostics, Nothing, newMember, EditKind.Update, span:=span) - classifier.ClassifyDeclarationBodyRudeUpdates(newMember) + Friend Overrides Sub ReportMemberOrLambdaBodyUpdateRudeEditsImpl(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), newDeclaration As SyntaxNode, newBody As SyntaxNode, span As TextSpan?) + Dim classifier = New EditClassifier(Me, diagnostics, Nothing, newDeclaration, EditKind.Update, span:=span) + classifier.ClassifyMemberOrLambdaBodyRudeUpdates(newBody) End Sub #End Region @@ -2484,6 +2493,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue If type.TypeKind = TypeKind.Module Then Return RudeEditKind.Insert End If + + Return RudeEditKind.None End Select ' All rude edits below only apply when inserting into an existing type (not when the type itself is inserted): @@ -2491,12 +2502,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return RudeEditKind.None End If - If newSymbol.ContainingType.Arity > 0 AndAlso newSymbol.Kind <> SymbolKind.NamedType Then - Return RudeEditKind.InsertIntoGenericType - End If - ' Inserting virtual or interface member is not allowed. - If (newSymbol.IsVirtual Or newSymbol.IsOverride Or newSymbol.IsAbstract) AndAlso newSymbol.Kind <> SymbolKind.NamedType Then + If newSymbol.IsVirtual Or newSymbol.IsOverride Or newSymbol.IsAbstract Then Return RudeEditKind.InsertVirtual End If @@ -2504,11 +2511,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Case SymbolKind.Method Dim method = DirectCast(newSymbol, IMethodSymbol) - ' Inserting generic method into an existing type is not allowed. - If method.Arity > 0 Then - Return RudeEditKind.InsertGenericMethod - End If - ' Inserting operator to an existing type is not allowed. If method.MethodKind = MethodKind.Conversion OrElse method.MethodKind = MethodKind.UserDefinedOperator Then Return RudeEditKind.InsertOperator @@ -2637,19 +2639,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxUtilities.IsIteratorMethodOrLambda(declaration) End Function - Protected Overrides Sub GetStateMachineInfo(body As SyntaxNode, ByRef suspensionPoints As ImmutableArray(Of SyntaxNode), ByRef kind As StateMachineKinds) + Friend Overrides Function GetStateMachineInfo(body As SyntaxNode) As StateMachineInfo ' In VB declaration and body are represented by the same node for both lambdas and methods (unlike C#) If SyntaxUtilities.IsAsyncMethodOrLambda(body) Then - suspensionPoints = SyntaxUtilities.GetAwaitExpressions(body) - kind = StateMachineKinds.Async + Return New StateMachineInfo(IsAsync:=True, IsIterator:=False, HasSuspensionPoints:=SyntaxUtilities.GetAwaitExpressions(body).Any()) ElseIf SyntaxUtilities.IsIteratorMethodOrLambda(body) Then - suspensionPoints = SyntaxUtilities.GetYieldStatements(body) - kind = StateMachineKinds.Iterator + Return New StateMachineInfo(IsAsync:=False, IsIterator:=True, HasSuspensionPoints:=SyntaxUtilities.GetYieldStatements(body).Any()) Else - suspensionPoints = ImmutableArray(Of SyntaxNode).Empty - kind = StateMachineKinds.None + Return StateMachineInfo.None End If - End Sub + End Function Friend Overrides Sub ReportStateMachineSuspensionPointRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), oldNode As SyntaxNode, newNode As SyntaxNode) ' TODO: changes around suspension points (foreach, lock, using, etc.) diff --git a/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb b/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb index effac2a33fc76..f43335ec91209 100644 --- a/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb +++ b/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb @@ -41,7 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GoToDefinition Case SyntaxKind.ExitFunctionStatement Case SyntaxKind.ExitPropertyStatement Dim Symbol = semanticModel.GetDeclaredSymbol(exitTarget) - Return Symbol.Locations.FirstOrNone().SourceSpan.Start + Return If(Symbol.Locations.FirstOrDefault()?.SourceSpan.Start, 0) End Select ' Exit Select, Exit While, Exit For, Exit ForEach, ... diff --git a/src/Features/VisualBasic/Portable/IntroduceParameter/VisualBasicIntroduceParameterCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/IntroduceParameter/VisualBasicIntroduceParameterCodeRefactoringProvider.vb index b94bf13772ff6..2d45e87ac9528 100644 --- a/src/Features/VisualBasic/Portable/IntroduceParameter/VisualBasicIntroduceParameterCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/IntroduceParameter/VisualBasicIntroduceParameterCodeRefactoringProvider.vb @@ -12,7 +12,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.IntroduceParameter Friend Class VisualBasicIntroduceParameterCodeRefactoringProvider - Inherits AbstractIntroduceParameterCodeRefactoringProvider(Of ExpressionSyntax, InvocationExpressionSyntax, ObjectCreationExpressionSyntax, IdentifierNameSyntax) + Inherits AbstractIntroduceParameterCodeRefactoringProvider(Of ExpressionSyntax, InvocationExpressionSyntax, ObjectCreationExpressionSyntax, IdentifierNameSyntax, ArgumentSyntax) @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.IntroduceParameter Return TryCast(variableDecl.Parent, LocalDeclarationStatementSyntax) End Function - Protected Overrides Function UpdateArgumentListSyntax(node As SyntaxNode, arguments As SeparatedSyntaxList(Of SyntaxNode)) As SyntaxNode + Protected Overrides Function UpdateArgumentListSyntax(node As SyntaxNode, arguments As SeparatedSyntaxList(Of ArgumentSyntax)) As SyntaxNode Return DirectCast(node, ArgumentListSyntax).WithArguments(arguments) End Function diff --git a/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj b/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj index 54ab536ae8bff..d6ed862178b3c 100644 --- a/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj +++ b/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj @@ -3,7 +3,7 @@ Library - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 full @@ -31,8 +31,10 @@ + + diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/AddAwait/AddAwaitTests.vb b/src/Features/VisualBasicTest/AddAwait/AddAwaitTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/AddAwait/AddAwaitTests.vb rename to src/Features/VisualBasicTest/AddAwait/AddAwaitTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb b/src/Features/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb rename to src/Features/VisualBasicTest/AddConstructorParametersFromMembers/AddConstructorParametersFromMembersTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb b/src/Features/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb rename to src/Features/VisualBasicTest/AddDebuggerDisplay/AddDebuggerDisplayTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/AddFileBanner/AddFileBannerTests.vb b/src/Features/VisualBasicTest/AddFileBanner/AddFileBannerTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/AddFileBanner/AddFileBannerTests.vb rename to src/Features/VisualBasicTest/AddFileBanner/AddFileBannerTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AbstractAddImportTests.vb b/src/Features/VisualBasicTest/AddImport/AbstractAddImportTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AbstractAddImportTests.vb rename to src/Features/VisualBasicTest/AddImport/AbstractAddImportTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests.vb b/src/Features/VisualBasicTest/AddImport/AddImportTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests.vb rename to src/Features/VisualBasicTest/AddImport/AddImportTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTestsWithAddImportDiagnosticProvider.vb b/src/Features/VisualBasicTest/AddImport/AddImportTestsWithAddImportDiagnosticProvider.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTestsWithAddImportDiagnosticProvider.vb rename to src/Features/VisualBasicTest/AddImport/AddImportTestsWithAddImportDiagnosticProvider.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb b/src/Features/VisualBasicTest/AddImport/AddImportTests_NuGet.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb rename to src/Features/VisualBasicTest/AddImport/AddImportTests_NuGet.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest.vb b/src/Features/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest.vb rename to src/Features/VisualBasicTest/CodeActions/AbstractVisualBasicCodeActionTest.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.vb b/src/Features/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.vb rename to src/Features/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToClassTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.vb b/src/Features/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.vb rename to src/Features/VisualBasicTest/ConvertAnonymousType/ConvertAnonymousTypeToTupleTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.vb b/src/Features/VisualBasicTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.vb rename to src/Features/VisualBasicTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb b/src/Features/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb rename to src/Features/VisualBasicTest/ConvertCast/ConvertDirectCastToTryCastTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb b/src/Features/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb rename to src/Features/VisualBasicTest/ConvertCast/ConvertTryCastToDirectCastTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertForEachToFor/ConvertForEachToForTests.vb b/src/Features/VisualBasicTest/ConvertForEachToFor/ConvertForEachToForTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertForEachToFor/ConvertForEachToForTests.vb rename to src/Features/VisualBasicTest/ConvertForEachToFor/ConvertForEachToForTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertForToForEach/ConvertForToForEachTests.vb b/src/Features/VisualBasicTest/ConvertForToForEach/ConvertForToForEachTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertForToForEach/ConvertForToForEachTests.vb rename to src/Features/VisualBasicTest/ConvertForToForEach/ConvertForToForEachTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.vb b/src/Features/VisualBasicTest/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.vb rename to src/Features/VisualBasicTest/ConvertIfToSwitch/ConvertIfToSwitchFixAllTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.vb b/src/Features/VisualBasicTest/ConvertIfToSwitch/ConvertIfToSwitchTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/ConvertIfToSwitch/ConvertIfToSwitchTests.vb rename to src/Features/VisualBasicTest/ConvertIfToSwitch/ConvertIfToSwitchTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb b/src/Features/VisualBasicTest/ConvertNumericLiteral/ConvertNumericLiteralTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/ConvertNumericLiteral/ConvertNumericLiteralTests.vb rename to src/Features/VisualBasicTest/ConvertNumericLiteral/ConvertNumericLiteralTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb b/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb rename to src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertConcatenationToInterpolatedStringTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.vb b/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.vb rename to src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertPlaceholderToInterpolatedStringTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.vb b/src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.vb rename to src/Features/VisualBasicTest/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb b/src/Features/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb rename to src/Features/VisualBasicTest/ConvertTupleToStruct/ConvertTupleToStructTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/CorrectNextControlVariable/CorrectNextControlVariableTests.vb b/src/Features/VisualBasicTest/CorrectNextControlVariable/CorrectNextControlVariableTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/CorrectNextControlVariable/CorrectNextControlVariableTests.vb rename to src/Features/VisualBasicTest/CorrectNextControlVariable/CorrectNextControlVariableTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/EmbeddedLanguages/ValidateJsonStringTests.vb b/src/Features/VisualBasicTest/EmbeddedLanguages/ValidateJsonStringTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/EmbeddedLanguages/ValidateJsonStringTests.vb rename to src/Features/VisualBasicTest/EmbeddedLanguages/ValidateJsonStringTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/EmbeddedLanguages/ValidateRegexStringTests.vb b/src/Features/VisualBasicTest/EmbeddedLanguages/ValidateRegexStringTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/EmbeddedLanguages/ValidateRegexStringTests.vb rename to src/Features/VisualBasicTest/EmbeddedLanguages/ValidateRegexStringTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/EncapsulateField/EncapsulateFieldTests.vb b/src/Features/VisualBasicTest/EncapsulateField/EncapsulateFieldTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/EncapsulateField/EncapsulateFieldTests.vb rename to src/Features/VisualBasicTest/EncapsulateField/EncapsulateFieldTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/ExitContinue/ExitContinueCodeActionTests.vb b/src/Features/VisualBasicTest/ExitContinue/ExitContinueCodeActionTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/ExitContinue/ExitContinueCodeActionTests.vb rename to src/Features/VisualBasicTest/ExitContinue/ExitContinueCodeActionTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ExtractMethod/ExtractMethodTests.vb b/src/Features/VisualBasicTest/ExtractMethod/ExtractMethodTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/ExtractMethod/ExtractMethodTests.vb rename to src/Features/VisualBasicTest/ExtractMethod/ExtractMethodTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/FixIncorrectFunctionReturnType/FixIncorrectFunctionReturnTypeTests.vb b/src/Features/VisualBasicTest/FixIncorrectFunctionReturnType/FixIncorrectFunctionReturnTypeTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/FixIncorrectFunctionReturnType/FixIncorrectFunctionReturnTypeTests.vb rename to src/Features/VisualBasicTest/FixIncorrectFunctionReturnType/FixIncorrectFunctionReturnTypeTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/FullyQualify/FullyQualifyTests.vb b/src/Features/VisualBasicTest/FullyQualify/FullyQualifyTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/FullyQualify/FullyQualifyTests.vb rename to src/Features/VisualBasicTest/FullyQualify/FullyQualifyTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.vb b/src/Features/VisualBasicTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.vb rename to src/Features/VisualBasicTest/GenerateComparisonOperators/GenerateComparisonOperatorsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb b/src/Features/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb rename to src/Features/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb b/src/Features/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb rename to src/Features/VisualBasicTest/GenerateDefaultConstructors/GenerateDefaultConstructorsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateEndConstruct/GenerateEndConstructTests.vb b/src/Features/VisualBasicTest/GenerateEndConstruct/GenerateEndConstructTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateEndConstruct/GenerateEndConstructTests.vb rename to src/Features/VisualBasicTest/GenerateEndConstruct/GenerateEndConstructTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateEnumMember/GenerateEnumMemberTests.vb b/src/Features/VisualBasicTest/GenerateEnumMember/GenerateEnumMemberTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateEnumMember/GenerateEnumMemberTests.vb rename to src/Features/VisualBasicTest/GenerateEnumMember/GenerateEnumMemberTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb b/src/Features/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb rename to src/Features/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateEvent/GenerateEventTests.vb b/src/Features/VisualBasicTest/GenerateEvent/GenerateEventTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateEvent/GenerateEventTests.vb rename to src/Features/VisualBasicTest/GenerateEvent/GenerateEventTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateConversionTests.vb b/src/Features/VisualBasicTest/GenerateMethod/GenerateConversionTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateConversionTests.vb rename to src/Features/VisualBasicTest/GenerateMethod/GenerateConversionTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb b/src/Features/VisualBasicTest/GenerateMethod/GenerateMethodTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb rename to src/Features/VisualBasicTest/GenerateMethod/GenerateMethodTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/GenerateOverrides/GenerateOverridesTests.vb b/src/Features/VisualBasicTest/GenerateOverrides/GenerateOverridesTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/GenerateOverrides/GenerateOverridesTests.vb rename to src/Features/VisualBasicTest/GenerateOverrides/GenerateOverridesTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb b/src/Features/VisualBasicTest/GenerateType/GenerateTypeTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb rename to src/Features/VisualBasicTest/GenerateType/GenerateTypeTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests_Dialog.vb b/src/Features/VisualBasicTest/GenerateType/GenerateTypeTests_Dialog.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests_Dialog.vb rename to src/Features/VisualBasicTest/GenerateType/GenerateTypeTests_Dialog.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb b/src/Features/VisualBasicTest/GenerateVariable/GenerateVariableTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateVariable/GenerateVariableTests.vb rename to src/Features/VisualBasicTest/GenerateVariable/GenerateVariableTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb b/src/Features/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb rename to src/Features/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.vb b/src/Features/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.vb rename to src/Features/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests_FixAllTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb b/src/Features/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb similarity index 99% rename from src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb rename to src/Features/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb index 0ae8cd521e165..0e3be0a29ed05 100644 --- a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb +++ b/src/Features/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb @@ -4098,7 +4098,7 @@ End Class", index:=1) End Function - Public Async Function TestDontImplementDisposePatternForLocallyDefinedIDisposable() As Task + Public Async Function TestDoNotImplementDisposePatternForLocallyDefinedIDisposable() As Task Await TestInRegularAndScriptAsync( "Namespace System Interface IDisposable @@ -4122,7 +4122,7 @@ End Namespace") End Function - Public Async Function TestDontImplementDisposePatternForStructures() As Task + Public Async Function TestDoNotImplementDisposePatternForStructures() As Task Await TestInRegularAndScriptAsync( "Imports System Structure S : Implements [|IDisposable|]", diff --git a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.vb b/src/Features/VisualBasicTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.vb rename to src/Features/VisualBasicTest/ImplementInterface/ImplementInterfaceTests_FixAllTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InitializeParameter/AddParameterCheckTests.vb b/src/Features/VisualBasicTest/InitializeParameter/AddParameterCheckTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InitializeParameter/AddParameterCheckTests.vb rename to src/Features/VisualBasicTest/InitializeParameter/AddParameterCheckTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InitializeParameter/InitializeMemberFromParameterTests.vb b/src/Features/VisualBasicTest/InitializeParameter/InitializeMemberFromParameterTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InitializeParameter/InitializeMemberFromParameterTests.vb rename to src/Features/VisualBasicTest/InitializeParameter/InitializeMemberFromParameterTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests.vb b/src/Features/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests.vb rename to src/Features/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests_CrossLanguage.vb b/src/Features/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests_CrossLanguage.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests_CrossLanguage.vb rename to src/Features/VisualBasicTest/InlineMethod/VisualBasicInlineMethodTests_CrossLanguage.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb b/src/Features/VisualBasicTest/InlineTemporary/InlineTemporaryTests.vb similarity index 98% rename from src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb rename to src/Features/VisualBasicTest/InlineTemporary/InlineTemporaryTests.vb index f1da4176fe910..3932cb5a412c9 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb +++ b/src/Features/VisualBasicTest/InlineTemporary/InlineTemporaryTests.vb @@ -111,7 +111,7 @@ Console.WriteLine(0) End Function - Public Async Function TestSingleDeclaratorDontRemoveLeadingTrivia1() As Task + Public Async Function TestSingleDeclaratorDoNotRemoveLeadingTrivia1() As Task Dim code = Imports System @@ -145,7 +145,7 @@ End Class End Function - Public Async Function TestSingleDeclaratorDontRemoveLeadingTrivia2() As Task + Public Async Function TestSingleDeclaratorDoNotRemoveLeadingTrivia2() As Task Dim code = Imports System @@ -175,7 +175,7 @@ End Class End Function - Public Async Function TestSingleDeclaratorDontMoveNextStatement() As Task + Public Async Function TestSingleDeclaratorDoNotMoveNextStatement() As Task Dim code = Module Program @@ -1146,7 +1146,7 @@ Dim f As Func(Of Integer) = Function() End Function - Public Async Function TestDontInlineTrailingComment() As Task + Public Async Function TestDoNotInlineTrailingComment() As Task Dim code = Dim [||]i As Integer = 1 + 1 ' First @@ -1163,7 +1163,7 @@ Console.WriteLine((1 + 1) * 2) End Function - Public Async Function TestDontRemoveLineBreakAfterComment() As Task + Public Async Function TestDoNotRemoveLineBreakAfterComment() As Task Dim code = Dim [||]x = 1 ' comment @@ -1197,7 +1197,7 @@ Console.WriteLine((1 + 1) * j) End Function - Public Async Function TestDontInsertUnnecessaryCast1() As Task + Public Async Function TestDoNotInsertUnnecessaryCast1() As Task Dim code = Dim [||]i As Object = 1 + 1 @@ -1213,7 +1213,7 @@ Dim j As Integer = 1 + 1 End Function - Public Async Function TestDontInsertUnnecessaryCast2() As Task + Public Async Function TestDoNotInsertUnnecessaryCast2() As Task Dim code = Dim [||]i As Integer = 1 + 1 @@ -1231,7 +1231,7 @@ Console.WriteLine(j) End Function - Public Async Function TestDontInsertUnnecessaryCast3() As Task + Public Async Function TestDoNotInsertUnnecessaryCast3() As Task Dim code = Dim [||]x As Action = Sub() @@ -1249,7 +1249,7 @@ Dim y As Action = Sub() End Function - Public Async Function TestDontInsertUnnecessaryCast4() As Task + Public Async Function TestDoNotInsertUnnecessaryCast4() As Task Dim code = Sub S @@ -1275,7 +1275,7 @@ End Sub End Function - Public Async Function TestDontInsertUnnecessaryCast5() As Task + Public Async Function TestDoNotInsertUnnecessaryCast5() As Task Dim code = Option Strict On @@ -1305,7 +1305,7 @@ End Module End Function - Public Async Function TestDontInsertUnnecessaryCast6() As Task + Public Async Function TestDoNotInsertUnnecessaryCast6() As Task Dim code = Option Infer On @@ -1337,7 +1337,7 @@ End Module End Function - Public Async Function TestDontInsertUnnecessaryCast7() As Task + Public Async Function TestDoNotInsertUnnecessaryCast7() As Task Dim code = Imports System @@ -1363,7 +1363,7 @@ End Module End Function - Public Async Function TestDontInsertUnnecessaryCast8() As Task + Public Async Function TestDoNotInsertUnnecessaryCast8() As Task Dim markup = Option Strict On @@ -1391,7 +1391,7 @@ End Module End Function - Public Async Function TestDontInsertUnnecessaryCast9() As Task + Public Async Function TestDoNotInsertUnnecessaryCast9() As Task Dim markup = Imports System.Collections.Generic @@ -1422,7 +1422,7 @@ End Module - Public Async Function TestDontInsertUnnecessaryCast10() As Task + Public Async Function TestDoNotInsertUnnecessaryCast10() As Task Dim markup = Imports System @@ -2606,7 +2606,7 @@ End Module End Function - Public Async Function TestDontOverparenthesizeXmlAttributeAccessExpression() As Task + Public Async Function TestDoNotOverparenthesizeXmlAttributeAccessExpression() As Task Dim code = Imports System.Xml.Linq @@ -2649,7 +2649,7 @@ End Module End Function - Public Async Function TestDontInlineInUnterminatedBlock() As Task + Public Async Function TestDoNotInlineInUnterminatedBlock() As Task Dim markup = Interface IGoo @@ -3700,7 +3700,7 @@ End Class End Function - Public Async Function TestBugfix_718152_DontRemoveParenthesisForAwaitExpression() As Task + Public Async Function TestBugfix_718152_DoNotRemoveParenthesisForAwaitExpression() As Task Dim code = Imports System @@ -4032,7 +4032,7 @@ End With End Function - Public Async Function TestDontParenthesizeInterpolatedStringWithNoInterpolation() As Task + Public Async Function TestDoNotParenthesizeInterpolatedStringWithNoInterpolation() As Task Dim code = Dim [||]s1 = $"hello" @@ -4048,7 +4048,7 @@ Dim s2 = AscW($"hello") End Function - Public Async Function TestDontParenthesizeInterpolatedStringWithInterpolation() As Task + Public Async Function TestDoNotParenthesizeInterpolatedStringWithInterpolation() As Task Dim code = Dim x = 42 diff --git a/src/EditorFeatures/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb b/src/Features/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb rename to src/Features/VisualBasicTest/IntroduceParameter/IntroduceParameterTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/IntroduceUsingStatement/IntroduceUsingStatementTests.vb b/src/Features/VisualBasicTest/IntroduceUsingStatement/IntroduceUsingStatementTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/IntroduceUsingStatement/IntroduceUsingStatementTests.vb rename to src/Features/VisualBasicTest/IntroduceUsingStatement/IntroduceUsingStatementTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/IntroduceVariable/IntroduceLocalForExpressionTests.vb b/src/Features/VisualBasicTest/IntroduceVariable/IntroduceLocalForExpressionTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/IntroduceVariable/IntroduceLocalForExpressionTests.vb rename to src/Features/VisualBasicTest/IntroduceVariable/IntroduceLocalForExpressionTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb b/src/Features/VisualBasicTest/IntroduceVariable/IntroduceVariableTests.vb similarity index 99% rename from src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb rename to src/Features/VisualBasicTest/IntroduceVariable/IntroduceVariableTests.vb index c3792f49f9b23..6ab9db0494d08 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb +++ b/src/Features/VisualBasicTest/IntroduceVariable/IntroduceVariableTests.vb @@ -2229,7 +2229,7 @@ End Class End Function - Public Async Function TestDontRemoveParenthesesIfOperatorPrecedenceWouldBeBroken() As Task + Public Async Function TestDoNotRemoveParenthesesIfOperatorPrecedenceWouldBeBroken() As Task Dim code = " Imports System @@ -2257,7 +2257,7 @@ End Module End Function - Public Async Function TestDontSimplifyParentUnlessEntireInnerNodeIsSelected() As Task + Public Async Function TestDoNotSimplifyParentUnlessEntireInnerNodeIsSelected() As Task Dim code = " Imports System diff --git a/src/EditorFeatures/VisualBasicTest/InvertConditional/InvertConditionalTests.vb b/src/Features/VisualBasicTest/InvertConditional/InvertConditionalTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InvertConditional/InvertConditionalTests.vb rename to src/Features/VisualBasicTest/InvertConditional/InvertConditionalTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb b/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb rename to src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb b/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb rename to src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/InvertLogical/InvertLogicalTests.vb b/src/Features/VisualBasicTest/InvertLogical/InvertLogicalTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/InvertLogical/InvertLogicalTests.vb rename to src/Features/VisualBasicTest/InvertLogical/InvertLogicalTests.vb diff --git a/src/Features/VisualBasicTest/Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests.vbproj b/src/Features/VisualBasicTest/Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests.vbproj new file mode 100644 index 0000000000000..3e24c59e8e94c --- /dev/null +++ b/src/Features/VisualBasicTest/Microsoft.CodeAnalysis.VisualBasic.Features.UnitTests.vbproj @@ -0,0 +1,69 @@ + + + + + Library + Off + Default + net472 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/EditorFeatures/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb b/src/Features/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb rename to src/Features/VisualBasicTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/MoveStaticMembers/VisualBasicMoveStaticMembersTests.vb b/src/Features/VisualBasicTest/MoveStaticMembers/VisualBasicMoveStaticMembersTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/MoveStaticMembers/VisualBasicMoveStaticMembersTests.vb rename to src/Features/VisualBasicTest/MoveStaticMembers/VisualBasicMoveStaticMembersTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/MoveToTopOfFile/MoveToTopOfFileTests.vb b/src/Features/VisualBasicTest/MoveToTopOfFile/MoveToTopOfFileTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/MoveToTopOfFile/MoveToTopOfFileTests.vb rename to src/Features/VisualBasicTest/MoveToTopOfFile/MoveToTopOfFileTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/BasicMoveTypeTestsBase.vb b/src/Features/VisualBasicTest/MoveType/BasicMoveTypeTestsBase.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/BasicMoveTypeTestsBase.vb rename to src/Features/VisualBasicTest/MoveType/BasicMoveTypeTestsBase.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.ActionCountTests.vb b/src/Features/VisualBasicTest/MoveType/MoveTypeTests.ActionCountTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.ActionCountTests.vb rename to src/Features/VisualBasicTest/MoveType/MoveTypeTests.ActionCountTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.vb b/src/Features/VisualBasicTest/MoveType/MoveTypeTests.MoveToNewFile.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.vb rename to src/Features/VisualBasicTest/MoveType/MoveTypeTests.MoveToNewFile.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.RenameFile.vb b/src/Features/VisualBasicTest/MoveType/MoveTypeTests.RenameFile.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.RenameFile.vb rename to src/Features/VisualBasicTest/MoveType/MoveTypeTests.RenameFile.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.RenameType.vb b/src/Features/VisualBasicTest/MoveType/MoveTypeTests.RenameType.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/MoveType/MoveTypeTests.RenameType.vb rename to src/Features/VisualBasicTest/MoveType/MoveTypeTests.RenameType.vb diff --git a/src/EditorFeatures/VisualBasicTest/NameTupleElement/NameTupleElementTests.vb b/src/Features/VisualBasicTest/NameTupleElement/NameTupleElementTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/NameTupleElement/NameTupleElementTests.vb rename to src/Features/VisualBasicTest/NameTupleElement/NameTupleElementTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/OverloadBase/OverloadBaseTests.vb b/src/Features/VisualBasicTest/OverloadBase/OverloadBaseTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/OverloadBase/OverloadBaseTests.vb rename to src/Features/VisualBasicTest/OverloadBase/OverloadBaseTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests.vb b/src/Features/VisualBasicTest/PreferFrameworkType/PreferFrameworkTypeTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests.vb rename to src/Features/VisualBasicTest/PreferFrameworkType/PreferFrameworkTypeTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.vb b/src/Features/VisualBasicTest/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.vb rename to src/Features/VisualBasicTest/PreferFrameworkType/PreferFrameworkTypeTests_FixAllTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/RemoveSharedFromModuleMembers/RemoveSharedFromModuleMembersTests.vb b/src/Features/VisualBasicTest/RemoveSharedFromModuleMembers/RemoveSharedFromModuleMembersTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/RemoveSharedFromModuleMembers/RemoveSharedFromModuleMembersTests.vb rename to src/Features/VisualBasicTest/RemoveSharedFromModuleMembers/RemoveSharedFromModuleMembersTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/RemoveUnusedVariable/RemoveUnusedVariableTest.vb b/src/Features/VisualBasicTest/RemoveUnusedVariable/RemoveUnusedVariableTest.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/RemoveUnusedVariable/RemoveUnusedVariableTest.vb rename to src/Features/VisualBasicTest/RemoveUnusedVariable/RemoveUnusedVariableTest.vb diff --git a/src/EditorFeatures/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb b/src/Features/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb rename to src/Features/VisualBasicTest/ReplaceConditionalWithStatements/ReplaceConditionalWithStatementsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.vb b/src/Features/VisualBasicTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.vb rename to src/Features/VisualBasicTest/ReplaceDocCommentTextWithTag/ReplaceDocCommentTextWithTagTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb b/src/Features/VisualBasicTest/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb rename to src/Features/VisualBasicTest/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb b/src/Features/VisualBasicTest/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb rename to src/Features/VisualBasicTest/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/SimplifyThisOrMe/SimplifyThisOrMeTests.vb b/src/Features/VisualBasicTest/SimplifyThisOrMe/SimplifyThisOrMeTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SimplifyThisOrMe/SimplifyThisOrMeTests.vb rename to src/Features/VisualBasicTest/SimplifyThisOrMe/SimplifyThisOrMeTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb b/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb similarity index 99% rename from src/EditorFeatures/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb rename to src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb index d8f523a04e7e7..f830ee5adb73a 100644 --- a/src/EditorFeatures/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb +++ b/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb @@ -1598,7 +1598,7 @@ End Namespace End Function - Public Async Function TestDontSimplifyNamesWhenThereAreParseErrors() As Task + Public Async Function TestDoNotSimplifyNamesWhenThereAreParseErrors() As Task Dim source = Imports System @@ -1749,7 +1749,7 @@ End Module End Function - Public Async Function TestDontUseAlias() As Task + Public Async Function TestDoNotUseAlias() As Task Dim source = Imports System @@ -1910,7 +1910,7 @@ End Module End Function - Public Async Function TestDontSimplifyNameSyntaxToTypeSyntaxInVBCref() As Task + Public Async Function TestDoNotSimplifyNameSyntaxToTypeSyntaxInVBCref() As Task Dim source = Imports System @@ -1923,7 +1923,7 @@ End Module End Function - Public Async Function TestDontSimplifyNameSyntaxToPredefinedTypeSyntaxInVBCref() As Task + Public Async Function TestDoNotSimplifyNameSyntaxToPredefinedTypeSyntaxInVBCref() As Task Dim source = Public Class Test @@ -1969,7 +1969,7 @@ Public Class Test_Dev11 End Function - Public Async Function TestDontSimplifyTypeNameBrokenCode() As Task + Public Async Function TestDoNotSimplifyTypeNameBrokenCode() As Task Dim source = - Public Async Function TestDontSimplifyAliases() As Task + Public Async Function TestDoNotSimplifyAliases() As Task Dim source = (TryStartAndInitializeProcessAsync, cacheResult: true); + _lazyInitializedService = AsyncLazy.Create(TryStartAndInitializeProcessAsync); _cancellationSource = new CancellationTokenSource(); InstanceId = instanceId; Options = options; @@ -65,7 +65,7 @@ private async Task TryStartAndInitializeProcessAsync(C { try { - var remoteService = await TryStartProcessAsync(Options.HostPath, Options.Culture, cancellationToken).ConfigureAwait(false); + var remoteService = await TryStartProcessAsync(Options.HostPath, Options.Culture, Options.UICulture, cancellationToken).ConfigureAwait(false); if (remoteService == null) { return default; @@ -134,7 +134,7 @@ private async Task TryStartAndInitializeProcessAsync(C } } - private async Task TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken) + private async Task TryStartProcessAsync(string hostPath, CultureInfo culture, CultureInfo uiCulture, CancellationToken cancellationToken) { int currentProcessId = Process.GetCurrentProcess().Id; var pipeName = typeof(InteractiveHost).FullName + Guid.NewGuid(); @@ -143,7 +143,7 @@ private async Task TryStartAndInitializeProcessAsync(C { StartInfo = new ProcessStartInfo(hostPath) { - Arguments = pipeName + " " + currentProcessId, + Arguments = $"{pipeName} {currentProcessId} \"{culture.Name}\" \"{uiCulture.Name}\"", WorkingDirectory = Host._initialWorkingDirectory, CreateNoWindow = true, UseShellExecute = false, @@ -211,7 +211,7 @@ void ProcessExitedBeforeEstablishingConnection(object sender, EventArgs e) platformInfo = (await jsonRpc.InvokeWithCancellationAsync( nameof(Service.InitializeAsync), - new object[] { Host._replServiceProviderType.AssemblyQualifiedName, culture.Name }, + new object[] { Host._replServiceProviderType.AssemblyQualifiedName }, cancellationToken).ConfigureAwait(false)).Deserialize(); } catch (Exception e) diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs index 15da788d3678e..5a30b0362c47a 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs @@ -160,13 +160,13 @@ public void Dispose() _serviceState = null; } - public Task InitializeAsync(string replServiceProviderTypeName, string cultureName) + public Task InitializeAsync(string replServiceProviderTypeName) { // TODO (tomat): we should share the copied files with the host var metadataFileProvider = new MetadataShadowCopyProvider( Path.Combine(Path.GetTempPath(), "InteractiveHostShadow"), noShadowCopyDirectories: s_systemNoShadowCopyDirectories, - documentationCommentsCulture: new CultureInfo(cultureName)); + documentationCommentsCulture: CultureInfo.CurrentUICulture); var assemblyLoader = new InteractiveAssemblyLoader(metadataFileProvider); var replServiceProviderType = Type.GetType(replServiceProviderTypeName); @@ -213,16 +213,10 @@ public void EmulateClientExit() s_clientExited.Set(); } - internal static Task RunServerAsync(string[] args, Func, object> invokeOnMainThread) - { - Contract.ThrowIfFalse(args.Length == 2, "Expecting arguments: "); - return RunServerAsync(args[0], int.Parse(args[1], CultureInfo.InvariantCulture), invokeOnMainThread); - } - /// /// Implements remote server. /// - private static async Task RunServerAsync(string pipeName, int clientProcessId, Func, object> invokeOnMainThread) + public static async Task RunServerAsync(string pipeName, int clientProcessId, Func, object> invokeOnMainThread) { if (!AttachToClientProcess(clientProcessId)) { @@ -538,7 +532,7 @@ private static void ReportUnhandledException(Exception e) foreach (var error in args.Errors) { var writer = (error.Severity == DiagnosticSeverity.Error) ? Console.Error : Console.Out; - writer.WriteLine(error.GetMessage(CultureInfo.CurrentCulture)); + writer.WriteLine(error.GetMessage(CultureInfo.CurrentUICulture)); } if (args.Errors.Length == 0) diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHostOptions.cs b/src/Interactive/Host/Interactive/Core/InteractiveHostOptions.cs index 961b9b1fa734b..3ecfbc9c8465c 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHostOptions.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHostOptions.cs @@ -24,10 +24,15 @@ internal sealed class InteractiveHostOptions public string? InitializationFilePath { get; } /// - /// Host culture used for localization of doc comments, errors. + /// Host culture used for data formatting. /// public CultureInfo Culture { get; } + /// + /// Host culture used for localization of doc comments, errors. + /// + public CultureInfo UICulture { get; } + /// /// Host process platform. /// @@ -37,6 +42,7 @@ internal sealed class InteractiveHostOptions string hostPath, string? initializationFilePath, CultureInfo culture, + CultureInfo uiCulture, InteractiveHostPlatform platform) { Contract.ThrowIfNull(hostPath); @@ -44,6 +50,7 @@ internal sealed class InteractiveHostOptions HostPath = hostPath; InitializationFilePath = initializationFilePath; Culture = culture; + UICulture = uiCulture; Platform = platform; } @@ -51,6 +58,7 @@ internal sealed class InteractiveHostOptions string hostDirectory, string? initializationFileName, CultureInfo culture, + CultureInfo uiCulture, InteractiveHostPlatform platform) { var hostSubdirectory = (platform == InteractiveHostPlatform.Core) ? "Core" : "Desktop"; @@ -59,7 +67,7 @@ internal sealed class InteractiveHostOptions var hostPath = Path.Combine(hostDirectory, hostSubdirectory, hostExecutableFileName); var initializationFilePath = (initializationFileName != null) ? Path.Combine(hostDirectory, hostSubdirectory, initializationFileName) : null; - return new InteractiveHostOptions(hostPath, initializationFilePath, culture, platform); + return new InteractiveHostOptions(hostPath, initializationFilePath, culture, uiCulture, platform); } } } diff --git a/src/Interactive/HostProcess/InteractiveHostEntryPoint.cs b/src/Interactive/HostProcess/InteractiveHostEntryPoint.cs index 23821c81066a6..6d1fe53cd9e2a 100644 --- a/src/Interactive/HostProcess/InteractiveHostEntryPoint.cs +++ b/src/Interactive/HostProcess/InteractiveHostEntryPoint.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.CodeAnalysis.ErrorReporting; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Interactive { @@ -20,6 +22,18 @@ private static async Task Main(string[] args) // Disables Windows Error Reporting for the process, so that the process fails fast. SetErrorMode(GetErrorMode() | ErrorMode.SEM_FAILCRITICALERRORS | ErrorMode.SEM_NOOPENFILEERRORBOX | ErrorMode.SEM_NOGPFAULTERRORBOX); + Contract.ThrowIfFalse(args.Length == 4, "Expecting arguments: "); + + var pipeName = args[0]; + var clientProcessId = int.Parse(args[1], CultureInfo.InvariantCulture); + var culture = new CultureInfo(args[2]); + var uiCulture = new CultureInfo(args[3]); + + CultureInfo.CurrentCulture = culture; + CultureInfo.CurrentUICulture = uiCulture; + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = uiCulture; + Control? control = null; using (var resetEvent = new ManualResetEventSlim(false)) { @@ -41,7 +55,7 @@ private static async Task Main(string[] args) try { - await InteractiveHost.Service.RunServerAsync(args, invokeOnMainThread).ConfigureAwait(false); + await InteractiveHost.Service.RunServerAsync(pipeName, clientProcessId, invokeOnMainThread).ConfigureAwait(false); return 0; } catch (Exception e) diff --git a/src/Interactive/HostTest/AbstractInteractiveHostTests.cs b/src/Interactive/HostTest/AbstractInteractiveHostTests.cs index 8caf29c80c693..901f037198305 100644 --- a/src/Interactive/HostTest/AbstractInteractiveHostTests.cs +++ b/src/Interactive/HostTest/AbstractInteractiveHostTests.cs @@ -76,7 +76,7 @@ public async Task InitializeAsync() { var initializationFileName = UseDefaultInitializationFile ? "CSharpInteractive.rsp" : null; - await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName, CultureInfo.InvariantCulture, DefaultPlatform)); + await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, DefaultPlatform)); // assert and remove logo: var output = SplitLines(await ReadOutputToEnd()); @@ -156,7 +156,7 @@ public async Task RestartHost() { ClearOutput(); - await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, InteractiveHostPlatform.Desktop64)); + await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, InteractiveHostPlatform.Desktop64)); } public async Task ReadOutputToEnd(bool isError = false) diff --git a/src/Interactive/HostTest/InteractiveHostDesktopTests.cs b/src/Interactive/HostTest/InteractiveHostDesktopTests.cs index b59bd4ac930fd..529ccf6831d7c 100644 --- a/src/Interactive/HostTest/InteractiveHostDesktopTests.cs +++ b/src/Interactive/HostTest/InteractiveHostDesktopTests.cs @@ -705,7 +705,7 @@ public async Task ReferencePathsRsp() var rspFile = rspDirectory.CreateFile("init.rsp"); rspFile.WriteAllText($"/lib:{directory1.Path} /r:Assembly2.dll {initFile.Path}"); - await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, culture: CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); + await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); var fxDir = await GetHostRuntimeDirectoryAsync(); await Execute(@" @@ -743,7 +743,7 @@ public async Task ReferencePathsRsp_Error() var rspFile = rspDirectory.CreateFile("init.rsp"); rspFile.WriteAllText($"{initFile.Path}"); - await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, culture: CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); + await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); var error = await ReadErrorOutputToEnd(); AssertEx.AssertEqualToleratingWhitespaceDifferences( @@ -768,7 +768,7 @@ public async Task DefaultUsings() /u:System.Text /u:System.Threading.Tasks "); - await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); + await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); await Execute(@" dynamic d = new ExpandoObject(); @@ -819,7 +819,7 @@ public async Task InitialScript_Error() {initFile.Path} "); - await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); + await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); await Execute("new Process()"); @@ -846,7 +846,7 @@ public async Task ScriptAndArguments() b c "); - await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); + await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, Host.OptionsOpt!.Platform)); var error = await ReadErrorOutputToEnd(); Assert.Equal("", error); @@ -1001,7 +1001,7 @@ public async Task Bitness() AssertEx.AssertEqualToleratingWhitespaceDifferences("", await ReadErrorOutputToEnd()); AssertEx.AssertEqualToleratingWhitespaceDifferences("8\r\n", await ReadOutputToEnd()); - await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, InteractiveHostPlatform.Desktop32)); + await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, InteractiveHostPlatform.Desktop32)); AssertEx.AssertEqualToleratingWhitespaceDifferences("", await ReadErrorOutputToEnd()); AssertEx.AssertEqualToleratingWhitespaceDifferences("", await ReadOutputToEnd()); @@ -1009,7 +1009,7 @@ public async Task Bitness() AssertEx.AssertEqualToleratingWhitespaceDifferences("", await ReadErrorOutputToEnd()); AssertEx.AssertEqualToleratingWhitespaceDifferences("4\r\n", await ReadOutputToEnd()); - var result = await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, InteractiveHostPlatform.Core)); + var result = await Host.ResetAsync(InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, InteractiveHostPlatform.Core)); AssertEx.AssertEqualToleratingWhitespaceDifferences("", await ReadErrorOutputToEnd()); AssertEx.AssertEqualToleratingWhitespaceDifferences("", await ReadOutputToEnd()); @@ -1018,6 +1018,29 @@ public async Task Bitness() AssertEx.AssertEqualToleratingWhitespaceDifferences("8\r\n", await ReadOutputToEnd()); } + [Fact] + public async Task Culture() + { + var rspFile = Temp.CreateFile(); + + var culture = new CultureInfo("cs-CZ"); + var uiCulture = CultureInfo.CurrentUICulture; + + await Host.ResetAsync(new InteractiveHostOptions(Host.OptionsOpt!.HostPath, rspFile.Path, culture, uiCulture, Host.OptionsOpt!.Platform)); + + await Host.ExecuteAsync(@"(1000.23).ToString(""C"")"); + + var error = await ReadErrorOutputToEnd(); + Assert.Equal("", error); + + var output = await ReadOutputToEnd(); + + AssertEx.AssertEqualToleratingWhitespaceDifferences($@" +{string.Format(InteractiveHostResources.Loading_context_from_0, Path.GetFileName(rspFile.Path))} +""1 000,23 Kč"" +", output); + } + #region Submission result printing - null/void/value. [Fact] diff --git a/src/Interactive/HostTest/StressTests.cs b/src/Interactive/HostTest/StressTests.cs index 11d6bbb35a1ff..a186fc0d2cc06 100644 --- a/src/Interactive/HostTest/StressTests.cs +++ b/src/Interactive/HostTest/StressTests.cs @@ -29,7 +29,7 @@ public async Task TestKill() private async Task TestKillAfterAsync(int milliseconds) { using var host = new InteractiveHost(typeof(CSharpReplServiceProvider), ".", millisecondsTimeout: 1, joinOutputWritingThreadsOnDisposal: true); - var options = InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, InteractiveHostPlatform.Desktop64); + var options = InteractiveHostOptions.CreateFromDirectory(TestUtils.HostRootPath, initializationFileName: null, CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, InteractiveHostPlatform.Desktop64); host.InteractiveHostProcessCreated += new Action(proc => { diff --git a/src/Interactive/csi/csi.csproj b/src/Interactive/csi/csi.csproj index e38d9b61d4408..85c48a4ea4942 100644 --- a/src/Interactive/csi/csi.csproj +++ b/src/Interactive/csi/csi.csproj @@ -5,7 +5,7 @@ Exe CSharpInteractive - net6.0;net472 + $(SourceBuildToolsetTargetFrameworks);net472 false true diff --git a/src/Interactive/vbi/vbi.vbproj b/src/Interactive/vbi/vbi.vbproj index e3ad0e2fe6026..723d361659ad3 100644 --- a/src/Interactive/vbi/vbi.vbproj +++ b/src/Interactive/vbi/vbi.vbproj @@ -5,7 +5,7 @@ Exe Sub Main - net6.0;net472 + $(SourceBuildToolsetTargetFrameworks) false false diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/Microsoft.Net.Compilers.Toolset.Package.csproj b/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/Microsoft.Net.Compilers.Toolset.Package.csproj index 690c01ea4ef2c..ad260df133883 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/Microsoft.Net.Compilers.Toolset.Package.csproj +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/Microsoft.Net.Compilers.Toolset.Package.csproj @@ -1,7 +1,7 @@  - net6.0;net472 + net472;$(SourceBuildToolsetTargetFramework) true Microsoft.Net.Compilers.Toolset @@ -24,7 +24,7 @@ $(NoWarn);NU5100;NU5128 <_DependsOn Condition="'$(TargetFramework)' == 'net472'">InitializeDesktopCompilerArtifacts - <_DependsOn Condition="'$(TargetFramework)' == 'net6.0'">InitializeCoreClrCompilerArtifacts + <_DependsOn Condition="'$(TargetFramework)' != 'net472'">InitializeCoreClrCompilerArtifacts @@ -38,8 +38,8 @@ Targets="Publish" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" - Condition="'$(TargetFramework)' == 'net6.0'" - SetTargetFramework="TargetFramework=net6.0" /> + Condition="'$(TargetFramework)' != 'net472'" + SetTargetFramework="TargetFramework=$(TargetFramework)" /> @@ -51,15 +51,15 @@ <_File Include="@(DesktopCompilerArtifact)" TargetDir="tasks/net472"/> <_File Include="@(DesktopCompilerResourceArtifact)" TargetDir="tasks/net472"/> - <_File Include="@(CoreClrCompilerBuildArtifact)" TargetDir="tasks/net6.0"/> - <_File Include="@(CoreClrCompilerToolsArtifact)" TargetDir="tasks/net6.0"/> - <_File Include="@(CoreClrCompilerBinArtifact)" TargetDir="tasks/net6.0/bincore"/> - <_File Include="@(CoreClrCompilerBinRuntimesArtifact)" TargetDir="tasks/net6.0/bincore/runtimes"/> + <_File Include="@(CoreClrCompilerBuildArtifact)" TargetDir="tasks/netcore"/> + <_File Include="@(CoreClrCompilerToolsArtifact)" TargetDir="tasks/netcore"/> + <_File Include="@(CoreClrCompilerBinArtifact)" TargetDir="tasks/netcore/bincore"/> + <_File Include="@(CoreClrCompilerBinRuntimesArtifact)" TargetDir="tasks/netcore/bincore/runtimes"/> - + diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props b/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props index 4b771afb964cf..8dc5f46a1ecd3 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props @@ -2,7 +2,7 @@ - <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' == 'Core'">net6.0 + <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' == 'Core'">netcore <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/CoreClrCompilerArtifacts.targets b/src/NuGet/Microsoft.Net.Compilers.Toolset/CoreClrCompilerArtifacts.targets index 71f57573d9622..509c97cb0bf8b 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/CoreClrCompilerArtifacts.targets +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/CoreClrCompilerArtifacts.targets @@ -3,37 +3,37 @@ - - - - + + + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - + + diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets b/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets index b99e993370284..aa85224f576fa 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/DesktopCompilerArtifacts.targets @@ -47,7 +47,7 @@ - + diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props b/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props index 4b771afb964cf..ea3dd999eb8c4 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props @@ -2,9 +2,7 @@ - <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' == 'Core'">net6.0 - <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 - <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ + <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\net472\ $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/Scripting/CSharp/Microsoft.CodeAnalysis.CSharp.Scripting.csproj b/src/Scripting/CSharp/Microsoft.CodeAnalysis.CSharp.Scripting.csproj index 9889361267dd9..9541674a3ed1c 100644 --- a/src/Scripting/CSharp/Microsoft.CodeAnalysis.CSharp.Scripting.csproj +++ b/src/Scripting/CSharp/Microsoft.CodeAnalysis.CSharp.Scripting.csproj @@ -4,7 +4,7 @@ Library Microsoft.CodeAnalysis.CSharp.Scripting - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 true diff --git a/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj b/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj index 3cc8b58ccfff1..ddf4325edf747 100644 --- a/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj +++ b/src/Scripting/Core/Microsoft.CodeAnalysis.Scripting.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.Scripting true - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 $(DefineConstants);SCRIPTING diff --git a/src/Scripting/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Scripting.vbproj b/src/Scripting/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Scripting.vbproj index 7d376fd92190b..17bc3b9d92ce9 100644 --- a/src/Scripting/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Scripting.vbproj +++ b/src/Scripting/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Scripting.vbproj @@ -3,7 +3,7 @@ Library - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 None false diff --git a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs index 2c70bfbe51e47..4e2c621a86750 100644 --- a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs +++ b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs @@ -138,16 +138,16 @@ private bool CheckPackages(TextWriter textWriter) allGood &= VerifyPackageCore( textWriter, FindNuGetPackage(Path.Combine(ArtifactsDirectory, "packages", Configuration, "Shipping"), "Microsoft.Net.Compilers.Toolset"), - excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\net6.0\bincore\Microsoft.DiaSymReader.Native", PathComparison), + excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.DiaSymReader.Native", PathComparison), (@"tasks\net472", GetProjectOutputDirectory("csc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("vbc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("csi", "net472")), (@"tasks\net472", GetProjectOutputDirectory("VBCSCompiler", "net472")), (@"tasks\net472", GetProjectOutputDirectory("Microsoft.Build.Tasks.CodeAnalysis", "net472")), - (@"tasks\net6.0\bincore", GetProjectPublishDirectory("csc", "net6.0")), - (@"tasks\net6.0\bincore", GetProjectPublishDirectory("vbc", "net6.0")), - (@"tasks\net6.0\bincore", GetProjectPublishDirectory("VBCSCompiler", "net6.0")), - (@"tasks\net6.0", GetProjectPublishDirectory("Microsoft.Build.Tasks.CodeAnalysis", "net6.0"))); + (@"tasks\netcore\bincore", GetProjectPublishDirectory("csc", "net6.0")), + (@"tasks\netcore\bincore", GetProjectPublishDirectory("vbc", "net6.0")), + (@"tasks\netcore\bincore", GetProjectPublishDirectory("VBCSCompiler", "net6.0")), + (@"tasks\netcore", GetProjectPublishDirectory("Microsoft.Build.Tasks.CodeAnalysis", "net6.0"))); foreach (var arch in new[] { "x86", "x64", "arm64" }) { diff --git a/src/Tools/BuildValidator/DemoLogger.cs b/src/Tools/BuildValidator/DemoLogger.cs index 052001ec4ef59..ac282792ccacd 100644 --- a/src/Tools/BuildValidator/DemoLogger.cs +++ b/src/Tools/BuildValidator/DemoLogger.cs @@ -39,7 +39,7 @@ public IDisposable BeginScope(TState state) public bool IsEnabled(LogLevel logLevel) => true; - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => LogCore(formatter(state, exception)); private void LogCore(string? message) @@ -70,7 +70,7 @@ public void Dispose() public bool IsEnabled(LogLevel logLevel) => false; - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { } } diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Classification/FSharpClassificationService.cs b/src/Tools/ExternalAccess/FSharp/Internal/Classification/FSharpClassificationService.cs index db19368560a9f..22b977d180562 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Classification/FSharpClassificationService.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Classification/FSharpClassificationService.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Classification; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; @@ -30,21 +31,21 @@ public FSharpClassificationService(IFSharpClassificationService service) _service = service; } - public void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { using var _ = s_listPool.GetPooledObject(out var list); _service.AddLexicalClassifications(text, textSpan, list, cancellationToken); result.AddRange(list); } - public async Task AddSemanticClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + public async Task AddSemanticClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { using var _ = s_listPool.GetPooledObject(out var list); await _service.AddSemanticClassificationsAsync(document, textSpan, list, cancellationToken).ConfigureAwait(false); result.AddRange(list); } - public async Task AddSyntacticClassificationsAsync(Document document, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public async Task AddSyntacticClassificationsAsync(Document document, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { using var _ = s_listPool.GetPooledObject(out var list); await _service.AddSyntacticClassificationsAsync(document, textSpan, list, cancellationToken).ConfigureAwait(false); @@ -56,7 +57,7 @@ public ClassifiedSpan AdjustStaleClassification(SourceText text, ClassifiedSpan return _service.AdjustStaleClassification(text, classifiedSpan); } - public void AddSyntacticClassifications(SolutionServices services, SyntaxNode root, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public void AddSyntacticClassifications(SolutionServices services, SyntaxNode root, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { // F# does not support syntax. } @@ -73,7 +74,7 @@ public void AddSyntacticClassifications(SolutionServices services, SyntaxNode ro return new(); } - public Task AddEmbeddedLanguageClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + public Task AddEmbeddedLanguageClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs index a154ef68110bf..9e28b6300647c 100644 --- a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs +++ b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs @@ -22,7 +22,7 @@ internal static class OmniSharpCodeFixContextFactory Action> registerCodeFix, OmniSharpCodeActionOptions options, CancellationToken cancellationToken) - => new(document, span, diagnostics, registerCodeFix, new DelegatingCodeActionOptionsProvider(options.GetCodeActionOptions), isBlocking: false, cancellationToken); + => new(document, span, diagnostics, registerCodeFix, new DelegatingCodeActionOptionsProvider(options.GetCodeActionOptions), cancellationToken); public static CodeAnalysis.CodeRefactorings.CodeRefactoringContext CreateCodeRefactoringContext( Document document, @@ -30,7 +30,7 @@ internal static class OmniSharpCodeFixContextFactory Action registerRefactoring, OmniSharpCodeActionOptions options, CancellationToken cancellationToken) - => new(document, span, registerRefactoring, new DelegatingCodeActionOptionsProvider(options.GetCodeActionOptions), isBlocking: false, cancellationToken); + => new(document, span, registerRefactoring, new DelegatingCodeActionOptionsProvider(options.GetCodeActionOptions), cancellationToken); public static FixAllContext CreateFixAllContext( Document? document, diff --git a/src/Tools/ExternalAccess/Razor/InternalAPI.Unshipped.txt b/src/Tools/ExternalAccess/Razor/InternalAPI.Unshipped.txt index d53b30c40479e..a8157c4c62a07 100644 --- a/src/Tools/ExternalAccess/Razor/InternalAPI.Unshipped.txt +++ b/src/Tools/ExternalAccess/Razor/InternalAPI.Unshipped.txt @@ -397,6 +397,7 @@ static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorPredefinedCodeRefactorin static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorPredefinedCodeRefactoringProviderNames.Wrapping.get -> string! static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteHostClient.TryGetClientAsync(Microsoft.CodeAnalysis.Host.HostWorkspaceServices! services, Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorServiceDescriptorsWrapper serviceDescriptors, Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteServiceCallbackDispatcherRegistry! callbackDispatchers, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task! static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteServiceCallbackIdWrapper.implicit operator Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteServiceCallbackIdWrapper(Microsoft.CodeAnalysis.Remote.RemoteServiceCallbackId id) -> Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteServiceCallbackIdWrapper +static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorSemanticTokensAccessor.GetTokenTypes(Microsoft.VisualStudio.LanguageServer.Protocol.ClientCapabilities! capabilities) -> System.Collections.Immutable.ImmutableArray static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorSemanticTokensAccessor.RoslynTokenTypes.get -> System.Collections.Immutable.ImmutableArray static Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorTestAnalyzerLoader.CreateAnalyzerAssemblyLoader() -> Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader! static readonly Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteServiceCallbackDispatcherRegistry.Empty -> Microsoft.CodeAnalysis.ExternalAccess.Razor.RazorRemoteServiceCallbackDispatcherRegistry! diff --git a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj index ced943330abbd..d10ea5b694045 100644 --- a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj +++ b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj @@ -23,6 +23,7 @@ + diff --git a/src/Tools/ExternalAccess/Razor/RazorSemanticTokensAccessor.cs b/src/Tools/ExternalAccess/Razor/RazorSemanticTokensAccessor.cs index 75316b40d64e6..63c36ea0d900a 100644 --- a/src/Tools/ExternalAccess/Razor/RazorSemanticTokensAccessor.cs +++ b/src/Tools/ExternalAccess/Razor/RazorSemanticTokensAccessor.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Text; using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -14,6 +11,9 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { internal class RazorSemanticTokensAccessor { - public static ImmutableArray RoslynTokenTypes => SemanticTokensHelpers.AllTokenTypes; + [Obsolete("Use GetTokenTypes")] + public static ImmutableArray RoslynTokenTypes => SemanticTokensSchema.LegacyTokenSchemaForRazor.AllTokenTypes; + + public static ImmutableArray GetTokenTypes(ClientCapabilities capabilities) => SemanticTokensSchema.GetSchema(capabilities).AllTokenTypes; } } diff --git a/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs b/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs index 17884b1529fd4..d79e6c2a5f1bc 100644 --- a/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs +++ b/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs @@ -83,7 +83,7 @@ protected static async Task> GetSemanticClassific var classifiers = service.GetDefaultSyntaxClassifiers(); var extensionManager = document.Project.Solution.Services.GetService(); - var results = ArrayBuilder.GetInstance(); + using var _ = Classifier.GetPooledList(out var results); await service.AddSemanticClassificationsAsync( document, @@ -94,7 +94,7 @@ protected static async Task> GetSemanticClassific results, CancellationToken.None); - return results.ToImmutableAndFree(); + return results.ToImmutableArray(); } } } diff --git a/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs b/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs index 4592b8f5bb49c..b2e34cd28db77 100644 --- a/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs +++ b/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs @@ -96,9 +96,9 @@ private async Task LoadSolutionAsync() protected static async Task> GetSemanticClassificationsAsync(Document document, TextSpan span) { var service = document.GetRequiredLanguageService(); - using var _ = ArrayBuilder.GetInstance(out var result); + using var _ = Classifier.GetPooledList(out var result); await service.AddSemanticClassificationsAsync(document, span, ClassificationOptions.Default, result, CancellationToken.None); - return result.ToImmutable(); + return result.ToImmutableArray(); } [Benchmark] diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj index 1496eff3c00ab..6ee9d977752c2 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj @@ -5,7 +5,7 @@ Exe Roslyn.Compilers.Internal.BoundTreeGenerator BoundTreeGenerator - net7.0 + net8.0 false \ No newline at end of file diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj index 509c6aaa7263b..63446bef0f887 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj @@ -4,7 +4,7 @@ Exe Roslyn.Compilers.CSharp.Internal.CSharpErrorFactsGenerator - net7.0 + net8.0 false \ No newline at end of file diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj index 6e61fb9da24b2..9f8b8b05f26e8 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj @@ -3,9 +3,9 @@ CSharpSyntaxGenerator - - net7.0;netstandard2.0 + net8.0;netstandard2.0 Exe $(RoslynPortableRuntimeIdentifiers) false diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj index d621b005959fe..a0f43f33de2db 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj @@ -6,7 +6,7 @@ Microsoft.CodeAnalysis.VisualBasic.Internal.VBErrorFactsGenerator VBErrorFactsGenerator - net7.0 + net8.0 false \ No newline at end of file diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj index 688663c362d0f..1cd0dc4b54904 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj @@ -7,7 +7,7 @@ Microsoft.CodeAnalysis.VisualBasic.Internal.VBSyntaxGenerator VBSyntaxGenerator Off - net7.0 + net8.0 false diff --git a/src/Tools/Source/RunTests/ProcessUtil.cs b/src/Tools/Source/RunTests/ProcessUtil.cs index 2df47acf51016..04e9d02d4992c 100644 --- a/src/Tools/Source/RunTests/ProcessUtil.cs +++ b/src/Tools/Source/RunTests/ProcessUtil.cs @@ -17,6 +17,12 @@ internal static class ProcessUtil { internal static int? TryGetParentProcessId(Process p) { + // System.Management is not supported outside of Windows. + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return null; + } + try { ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'"); diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml index 8e77154be482f..a117c2c817d8b 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml @@ -127,15 +127,7 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Add_missing_using_directives_on_paste}" /> - - - - - - - + diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index 7c994e4644c98..c6726f44dbaec 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -100,13 +100,6 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(SuggestForTypesInNuGetPackages, SymbolSearchOptionsStorage.SearchNuGetPackages, LanguageNames.CSharp); BindToOption(AddUsingsOnPaste, AddImportOnPasteOptionsStorage.AddImportsOnPaste, LanguageNames.CSharp); - // Quick Actions - BindToOption(ComputeQuickActionsAsynchronouslyExperimental, SuggestionsOptionsStorage.Asynchronous, () => - { - // If the option has not been set by the user, check if the option is disabled from experimentation. - return !optionStore.GetOption(SuggestionsOptionsStorage.AsynchronousQuickActionsDisableFeatureFlag); - }); - // Highlighting BindToOption(EnableHighlightReferences, ReferenceHighlightingOptionsStorage.ReferenceHighlighting, LanguageNames.CSharp); BindToOption(EnableHighlightKeywords, KeywordHighlightingOptionsStorage.KeywordHighlighting, LanguageNames.CSharp); @@ -165,7 +158,7 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(Editor_color_scheme, ColorSchemeOptionsStorage.ColorScheme); // Extract Method - BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptionsStorage.DontPutOutOrRefOnStruct, LanguageNames.CSharp); + BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptionsStorage.DoNotPutOutOrRefOnStruct, LanguageNames.CSharp); // Implement Interface or Abstract Class BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptionsStorage.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.CSharp); diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs index 6db8181cadeef..581c95c010c3d 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs @@ -225,9 +225,6 @@ public static string Option_OptimizeForSolutionSize_Small public static string Option_Quick_Actions => ServicesVSResources.Quick_Actions; - public static string Option_Compute_Quick_Actions_asynchronously_experimental - => ServicesVSResources.Compute_Quick_Actions_asynchronously_experimental; - public static string Option_Outlining => ServicesVSResources.Outlining; diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.cs index 009664cca738f..79e9089b91f1b 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.cs @@ -16,8 +16,8 @@ public int ExtractMethod_AllowBestEffort public int ExtractMethod_DoNotPutOutOrRefOnStruct { - get { return GetBooleanOption(ExtractMethodOptionsStorage.DontPutOutOrRefOnStruct); } - set { SetBooleanOption(ExtractMethodOptionsStorage.DontPutOutOrRefOnStruct, value); } + get { return GetBooleanOption(ExtractMethodOptionsStorage.DoNotPutOutOrRefOnStruct); } + set { SetBooleanOption(ExtractMethodOptionsStorage.DoNotPutOutOrRefOnStruct, value); } } } } diff --git a/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs b/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs index 363a345338c0c..c40a8ddd7601c 100644 --- a/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs +++ b/src/VisualStudio/CSharp/Test/DesignerAttribute/DesignerAttributeServiceTests.cs @@ -88,7 +88,7 @@ private static async Task TestAsync(string codeWithMarker, string? category) var compilation = await document.Project.GetRequiredCompilationAsync(CancellationToken.None); var actual = await DesignerAttributeDiscoveryService.ComputeDesignerAttributeCategoryAsync( - compilation.DesignerCategoryAttributeType() != null, document, CancellationToken.None); + compilation.DesignerCategoryAttributeType() != null, document.Project, document.Id, CancellationToken.None); Assert.Equal(category, actual); } diff --git a/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs b/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs index cd99e82d9031e..c50b845d25cb7 100644 --- a/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs +++ b/src/VisualStudio/CSharp/Test/PersistentStorage/AbstractPersistentStorageTests.cs @@ -885,7 +885,7 @@ public async Task TestPersistSyntaxTreeIndex([CombinatorialRange(0, Iterations)] await using (var storage = await GetStorageAsync(solution)) { var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, default); - await index.SaveAsync(_storageService!, document, default); + await index.SaveAsync(document, _storageService!); var index2 = await SyntaxTreeIndex.LoadAsync(_storageService!, DocumentKey.ToDocumentKey(document), checksum: null, new StringTable(), default); Assert.NotNull(index2); @@ -905,7 +905,7 @@ public async Task TestPersistTopLevelSyntaxTreeIndex([CombinatorialRange(0, Iter await using (var storage = await GetStorageAsync(solution)) { var index = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync(document, default); - await index.SaveAsync(_storageService!, document, default); + await index.SaveAsync(document, _storageService!); var index2 = await TopLevelSyntaxTreeIndex.LoadAsync(_storageService!, DocumentKey.ToDocumentKey(document), checksum: null, new StringTable(), default); Assert.NotNull(index2); diff --git a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs index 4b76065d338df..59ad0a82e6dd3 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerService.cs @@ -384,19 +384,20 @@ void HandleProjectsWithDisabledAnalysis() : projects.Where(p => !p.State.RunAnalyzers).ToImmutableArrayOrEmpty(); if (!projectsWithDisabledAnalysis.IsEmpty) { - // Compute diagnostics by overidding project's RunCodeAnalysis flag to true. + // Compute diagnostics by overriding project's RunCodeAnalysis flag to true. var tasks = new System.Threading.Tasks.Task>[projectsWithDisabledAnalysis.Length]; for (var index = 0; index < projectsWithDisabledAnalysis.Length; index++) { var project = projectsWithDisabledAnalysis[index]; project = project.Solution.WithRunAnalyzers(project.Id, runAnalyzers: true).GetProject(project.Id)!; tasks[index] = Task.Run( - () => _diagnosticService.GetDiagnosticsAsync(project.Solution, project.Id)); + () => _diagnosticService.GetDiagnosticsAsync(project.Solution, project.Id, documentId: null, + includeSuppressedDiagnostics: false, includeNonLocalDocumentDiagnostics: true, CancellationToken.None)); } Task.WhenAll(tasks).Wait(); - // Report new host diagostics. + // Report new host diagnostics. for (var index = 0; index < projectsWithDisabledAnalysis.Length; index++) { var project = projectsWithDisabledAnalysis[index]; diff --git a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs index aba42d4ba077d..a4f67e2acd517 100644 --- a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.cs +++ b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceMarginTaggerProvider.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.ComponentModel.Composition; using System.Linq; @@ -12,7 +11,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; @@ -35,7 +33,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.InheritanceMarg [TagType(typeof(InheritanceMarginTag))] [ContentType(ContentTypeNames.RoslynContentType)] [Name(nameof(InheritanceMarginTaggerProvider))] - internal sealed class InheritanceMarginTaggerProvider : AsynchronousViewTaggerProvider + internal sealed class InheritanceMarginTaggerProvider : AsynchronousViewportTaggerProvider { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -71,24 +69,9 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex TaggerEventSources.OnGlobalOptionChanged(GlobalOptions, InheritanceMarginOptionsStorage.InheritanceMarginCombinedWithIndicatorMargin)); } - protected override IEnumerable GetSpansToTag(ITextView? textView, ITextBuffer subjectBuffer) - { - this.ThreadingContext.ThrowIfNotOnUIThread(); - Contract.ThrowIfNull(textView); - - var visibleSpan = textView.GetVisibleLinesSpan(subjectBuffer, extraLines: 100); - if (visibleSpan == null) - { - return base.GetSpansToTag(textView, subjectBuffer); - } - - return SpecializedCollections.SingletonEnumerable(visibleSpan.Value); - } - protected override async Task ProduceTagsAsync( TaggerContext context, DocumentSnapshotSpan spanToTag, - int? caretPosition, CancellationToken cancellationToken) { var document = spanToTag.Document; diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 50cb04c3d5c1f..4ae1a89071acd 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -237,8 +237,8 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_using_directive_placement", new RoamingProfileStorage("TextEditor.CSharp.Specific.PreferredUsingDirectivePlacement")}, {"dotnet_provide_date_and_time_completions", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ProvideDateAndTimeCompletions")}, {"dotnet_log_telemetry_for_background_analyzer_execution", new FeatureFlagStorage(@"Roslyn.LogTelemetryForBackgroundAnalyzerExecution")}, + {"dotnet_lightbulb_skip_executing_deprioritized_analyzers", new FeatureFlagStorage(@"Roslyn.LightbulbSkipExecutingDeprioritizedAnalyzers")}, {"dotnet_enable_lsp_pull_diagnostics", new FeatureFlagStorage(@"Lsp.PullDiagnostics")}, - {"dotnet_pull_diagnostic_tagging", new FeatureFlagStorage(@"Roslyn.PullDiagnosticTagging")}, #pragma warning disable CS0612 // Type or member is obsolete {"dotnet_auto_xml_doc_comment_generation", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.Automatic XML Doc Comment Generation", "TextEditor.VisualBasic.Specific.AutoComment")}, #pragma warning restore @@ -333,18 +333,18 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_inline_diagnostics_location", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineDiagnostics.LocationOption")}, {"dotnet_colorize_inline_hints", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ColorHints")}, {"dotnet_display_inline_hints_while_pressing_alt_f1", new RoamingProfileStorage("TextEditor.Specific.DisplayAllHintsWhilePressingAltF1")}, - {"dotnet_enable_inline_hints_for_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints")}, - {"csharp_enable_inline_hints_for_types", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints")}, - {"csharp_enable_inline_hints_for_implicit_object_creation", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitObjectCreation")}, - {"csharp_enable_inline_hints_for_implicit_variable_types", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitVariableTypes")}, - {"dotnet_enable_inline_hints_for_indexer_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForArrayIndexers")}, - {"csharp_enable_inline_hints_for_lambda_parameter_types", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForLambdaParameterTypes")}, - {"dotnet_enable_inline_hints_for_literal_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForLiteralParameters")}, - {"dotnet_enable_inline_hints_for_object_creation_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForObjectCreationParameters")}, - {"dotnet_enable_inline_hints_for_other_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForOtherParameters")}, - {"dotnet_suppress_inline_hints_for_parameters_that_differ_only_by_suffix", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatDifferOnlyBySuffix")}, - {"dotnet_suppress_inline_hints_for_parameters_that_match_argument_name", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchArgumentName")}, - {"dotnet_suppress_inline_hints_for_parameters_that_match_method_intent", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchMethodIntent")}, + {"dotnet_enable_inlay_hints_for_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints")}, + {"csharp_enable_inlay_hints_for_types", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints")}, + {"csharp_enable_inlay_hints_for_implicit_object_creation", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitObjectCreation")}, + {"csharp_enable_inlay_hints_for_implicit_variable_types", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitVariableTypes")}, + {"dotnet_enable_inlay_hints_for_indexer_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForArrayIndexers")}, + {"csharp_enable_inlay_hints_for_lambda_parameter_types", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForLambdaParameterTypes")}, + {"dotnet_enable_inlay_hints_for_literal_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForLiteralParameters")}, + {"dotnet_enable_inlay_hints_for_object_creation_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForObjectCreationParameters")}, + {"dotnet_enable_inlay_hints_for_other_parameters", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForOtherParameters")}, + {"dotnet_suppress_inlay_hints_for_parameters_that_differ_only_by_suffix", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatDifferOnlyBySuffix")}, + {"dotnet_suppress_inlay_hints_for_parameters_that_match_argument_name", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchArgumentName")}, + {"dotnet_suppress_inlay_hints_for_parameters_that_match_method_intent", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchMethodIntent")}, {"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")}, @@ -408,8 +408,6 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"csharp_split_string_literal_on_return", new RoamingProfileStorage("TextEditor.CSharp.Specific.SplitStringLiterals")}, {"visual_studio_open_stack_trace_explorer_on_focus", new RoamingProfileStorage("StackTraceExplorer.Options.OpenOnFocus")}, {"visual_studio_enable_document_outline", new RoamingProfileStorage(@"DocumentOutline.Enable")}, - {"dotnet_enable_asynchronous_suggestions", new RoamingProfileStorage("TextEditor.Specific.Suggestions.Asynchronous4")}, - {"dotnet_disable_asynchronous_quick_actions", new FeatureFlagStorage(@"Roslyn.AsynchronousQuickActionsDisable2")}, {"visual_studio_enable_symbol_search", new LocalUserProfileStorage(@"Roslyn\Features\SymbolSearch", "Enabled")}, {"dotnet_search_nuget_packages", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.SuggestForTypesInNuGetPackages")}, {"dotnet_search_reference_assemblies", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.SuggestForTypesInReferenceAssemblies")}, @@ -428,6 +426,8 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"visual_studio_navigate_to_object_browser", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.NavigateToObjectBrowser")}, {"visual_studio_workspace_partial_load_mode", new FeatureFlagStorage(@"Roslyn.PartialLoadMode")}, {"dotnet_disable_shared_syntax_trees", new FeatureFlagStorage(@"Roslyn.DisableSharedSyntaxTrees")}, + {"dotnet_defer_creating_recoverable_text", new FeatureFlagStorage(@"Roslyn.DeferCreatingRecoverableText")}, + {"dotnet_disable_recoverable_text", new FeatureFlagStorage(@"Roslyn.DisableRecoverableText")}, {"dotnet_enable_diagnostics_in_source_generated_files", new RoamingProfileStorage("TextEditor.Roslyn.Specific.EnableDiagnosticsInSourceGeneratedFilesExperiment")}, {"dotnet_enable_diagnostics_in_source_generated_files_feature_flag", new FeatureFlagStorage(@"Roslyn.EnableDiagnosticsInSourceGeneratedFiles")}, {"dotnet_enable_opening_source_generated_files_in_workspace", new RoamingProfileStorage("TextEditor.Roslyn.Specific.EnableOpeningSourceGeneratedFilesInWorkspaceExperiment")}, diff --git a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef index bb95c44a768af..8b73440802722 100644 --- a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef +++ b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef @@ -31,6 +31,12 @@ "Title"="Run C#/VB code analysis on .Net 6 (requires restart)" "PreviewPaneChannels"="IntPreview,int.main" +[$RootKey$\FeatureFlags\Roslyn\OOPServerGC] +"Description"="Run C#/VB out-of-process code analysis with ServerGC." +"Value"=dword:00000000 +"Title"="Run C#/VB code analysis with ServerGC (requires restart)" +"PreviewPaneChannels"="IntPreview,int.main" + // Corresponds to WellKnownExperimentNames.LspPullDiagnosticsFeatureFlag [$RootKey$\FeatureFlags\Lsp\PullDiagnostics] "Description"="Enables the LSP-powered diagnostics for managed .Net projects" diff --git a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs index 4796090c36d63..8baac6fe6c737 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/FileChangeTracker.cs @@ -130,7 +130,7 @@ public Task StartFileChangeListeningAsync() { return null; } - }, cacheResult: true); + }); lock (s_lastBackgroundTaskGate) { diff --git a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs index dbe211ae53c5b..6fdfa6712f82a 100644 --- a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs @@ -5,9 +5,11 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Preview; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; @@ -34,6 +36,9 @@ internal sealed class Factory : IWorkspaceServiceFactory private readonly IGlobalOptionService _globalOptions; private readonly IThreadingContext _threadingContext; + private readonly object _gate = new(); + private VisualStudioRemoteHostClientProvider? _cachedVSInstance; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public Factory( @@ -56,18 +61,33 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) // We don't want to bring up the OOP process in a VS cloud environment client instance // Avoids proffering brokered services on the client instance. if (!_globalOptions.GetOption(RemoteHostOptionsStorage.OOP64Bit) || - workspaceServices.Workspace is not VisualStudioWorkspace || + workspaceServices.Workspace is not (VisualStudioWorkspace or PreviewWorkspace) || workspaceServices.GetRequiredService().IsCloudEnvironmentClient()) { // Run code in the current process return new DefaultRemoteHostClientProvider(); } - return new VisualStudioRemoteHostClientProvider(workspaceServices.SolutionServices, _globalOptions, _vsServiceProvider, _threadingContext, _listenerProvider, _callbackDispatchers); + lock (_gate) + { + // If we have a cached vs instance, and the workspace this is for uses the same host services, then + // we can return that instance. + if (_cachedVSInstance?.Services.WorkspaceServices.HostServices == workspaceServices.SolutionServices.WorkspaceServices.HostServices) + return _cachedVSInstance; + + // Otherwise, we either don't have a cached vs instance, or this is for a different set of host + // services. Make a fresh provider. If this was for the VSWorkspace, then cache this for future + // callers with the same services. + var provider = new VisualStudioRemoteHostClientProvider(workspaceServices.SolutionServices, _globalOptions, _vsServiceProvider, _threadingContext, _listenerProvider, _callbackDispatchers); + if (workspaceServices.Workspace is VisualStudioWorkspace) + _cachedVSInstance = provider; + + return provider; + } } } - private readonly SolutionServices _services; + public readonly SolutionServices Services; private readonly IGlobalOptionService _globalOptions; private readonly VSThreading.AsyncLazy _lazyClient; private readonly IAsyncServiceProvider _vsServiceProvider; @@ -83,7 +103,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) AsynchronousOperationListenerProvider listenerProvider, RemoteServiceCallbackDispatcherRegistry callbackDispatchers) { - _services = services; + Services = services; _globalOptions = globalOptions; _vsServiceProvider = vsServiceProvider; _threadingContext = threadingContext; @@ -108,10 +128,10 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) (_globalOptions.GetOption(SolutionCrawlerRegistrationService.EnableSolutionCrawler) ? RemoteProcessConfiguration.EnableSolutionCrawler : 0); // VS AsyncLazy does not currently support cancellation: - var client = await ServiceHubRemoteHostClient.CreateAsync(_services, configuration, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); + var client = await ServiceHubRemoteHostClient.CreateAsync(Services, configuration, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); // proffer in-proc brokered services: - _ = brokeredServiceContainer.Proffer(SolutionAssetProvider.ServiceDescriptor, (_, _, _, _) => ValueTaskFactory.FromResult(new SolutionAssetProvider(_services))); + _ = brokeredServiceContainer.Proffer(SolutionAssetProvider.ServiceDescriptor, (_, _, _, _) => ValueTaskFactory.FromResult(new SolutionAssetProvider(Services))); return client; } diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index 2be8569761649..c275b06aa905c 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1813,9 +1813,6 @@ Additional information: {1} Run code analysis in separate process (requires restart) - - Compute Quick Actions asynchronously (experimental, requires restart) - Quick Actions diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs index 484c26ebaaa59..6b204d0ee7146 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioDiagnosticListTableCommandHandler.cs @@ -218,7 +218,7 @@ private async Task SetSeverityHandlerAsync(ReportDiagnostic reportDiagnostic, Di // Kick off diagnostic re-analysis for affected document so that the configured diagnostic gets refreshed. _ = Task.Run(() => { - _diagnosticService.Reanalyze(_workspace, documentIds: SpecializedCollections.SingletonEnumerable(selectedDiagnostic.DocumentId), highPriority: true); + _diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(selectedDiagnostic.DocumentId), highPriority: true); }); } } diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 0f9c27422724d..26119b38ebc07 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -403,7 +403,7 @@ private async Task ApplySuppressionFixAsync(IEnumerable? diagnos _ = Task.Run(() => { var reanalyzeDocuments = diagnosticsToFix.Select(d => d.DocumentId).WhereNotNull().Distinct(); - _diagnosticService.Reanalyze(_workspace, documentIds: reanalyzeDocuments, highPriority: true); + _diagnosticService.Reanalyze(_workspace, projectIds: null, documentIds: reanalyzeDocuments, highPriority: true); }); } } @@ -546,7 +546,8 @@ private static IEnumerable FilterDiagnostics(IEnumerable kvp.Value.Select(d => d.Id)).ToImmutableHashSet(); - var latestProjectDiagnostics = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, cancellationToken: cancellationToken) + var latestProjectDiagnostics = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, + diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)).Where(IsDocumentDiagnostic); latestDocumentDiagnosticsMap.Clear(); @@ -635,7 +636,8 @@ private static IEnumerable FilterDiagnostics(IEnumerable d.Id).ToImmutableHashSet(); - var latestDiagnosticsFromDiagnosticService = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, cancellationToken: cancellationToken) + var latestDiagnosticsFromDiagnosticService = (await _diagnosticService.GetDiagnosticsForIdsAsync(project.Solution, project.Id, documentId: null, + diagnosticIds: uniqueDiagnosticIds, includeSuppressedDiagnostics: true, includeNonLocalDocumentDiagnostics: true, cancellationToken) .ConfigureAwait(false)); latestDiagnosticsToFix.Clear(); diff --git a/src/VisualStudio/Core/Def/Venus/ContainedDocument.DocumentServiceProvider.cs b/src/VisualStudio/Core/Def/Venus/ContainedDocument.DocumentServiceProvider.cs index 956b94eab0c98..339ad68efabad 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedDocument.DocumentServiceProvider.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedDocument.DocumentServiceProvider.cs @@ -184,7 +184,7 @@ public DocumentExcerpter(ITextBuffer primaryBuffer) // anything based on content is starting from 0 var startPositionOnContentSpan = GetNonWhitespaceStartPositionOnContent(contentSpanOnPrimarySnapshot); - using var _1 = ArrayBuilder.GetInstance(out var list); + using var _1 = Classifier.GetPooledList(out var list); foreach (var roslynSpan in primarySnapshot.MapToSourceSnapshots(contentSpanOnPrimarySnapshot.Span)) { @@ -228,7 +228,7 @@ public DocumentExcerpter(ITextBuffer primaryBuffer) // // the EditorClassifier call above fills all the gaps for the span it is called with, but we are combining // multiple spans with html code, so we need to fill those gaps - using var _2 = ArrayBuilder.GetInstance(out var builder); + using var _2 = Classifier.GetPooledList(out var builder); ClassifierHelper.FillInClassifiedSpanGaps(startPositionOnContentSpan, list, builder); // add html after roslyn content if there is any @@ -246,7 +246,7 @@ public DocumentExcerpter(ITextBuffer primaryBuffer) } } - return builder.ToImmutable(); + return builder.ToImmutableArray(); } private static int GetNonWhitespaceStartPositionOnContent(SnapshotSpan spanOnPrimarySnapshot) diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index e8b67d2c97d42..8c80debd9acd8 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, documentIds: SpecializedCollections.SingletonEnumerable(this.ContainedDocument.Id)); + _diagnosticAnalyzerService.Reanalyze(this.Workspace, projectIds: null, documentIds: SpecializedCollections.SingletonEnumerable(this.ContainedDocument.Id), highPriority: false); } public string GetFilePathFromBuffers() diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index ca10e1c86e09f..4e3f516cc527b 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -287,11 +287,6 @@ Komentáře - - Compute Quick Actions asynchronously (experimental, requires restart) - Asynchronní výpočet rychlých akcí (experimentální, vyžaduje restartování) - - Containing Member Obsahující člen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 7d1bd1822077b..bb6ace886c612 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -287,11 +287,6 @@ Kommentare - - Compute Quick Actions asynchronously (experimental, requires restart) - Schnelle Aktionen asynchron berechnen (experimentell, Neustart erforderlich) - - Containing Member Enthaltender Member diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index d974e12d5bd89..e498d9609eb02 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -287,11 +287,6 @@ Comentarios - - Compute Quick Actions asynchronously (experimental, requires restart) - Procesar acciones rápidas de forma asincrónica (experimental, requiere reinicio) - - Containing Member Miembro contenedor diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index a07c00e9c982b..9ef8d74b4d168 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -287,11 +287,6 @@ Commentaires - - Compute Quick Actions asynchronously (experimental, requires restart) - Calculer les actions rapides de manière asynchrone (expérimental, requiert un redémarrage) - - Containing Member Membre conteneur diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index e99bacbfe6453..ec360d23135c5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -287,11 +287,6 @@ Commenti - - Compute Quick Actions asynchronously (experimental, requires restart) - Calcola Azioni rapide in modo asincrono (sperimentale, richiede il riavvio) - - Containing Member Membro contenitore diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 9ce596f887cf4..0d1336718f1f4 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -287,11 +287,6 @@ コメント - - Compute Quick Actions asynchronously (experimental, requires restart) - クイック アクションを非同期で計算する (試験的、再起動が必要) - - Containing Member 含んでいるメンバー diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index d7c2f386bb09a..349f24c071817 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -287,11 +287,6 @@ 설명 - - Compute Quick Actions asynchronously (experimental, requires restart) - 빠른 작업을 비동기적으로 컴퓨팅(실험적, 다시 시작해야 함) - - Containing Member 포함하는 멤버 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index db4e48fa2f0ee..91c994e7304db 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -287,11 +287,6 @@ Komentarze - - Compute Quick Actions asynchronously (experimental, requires restart) - Asynchroniczne szybkie akcje środowiska obliczeniowego (eksperymentalne, wymaga ponownego uruchomienia) - - Containing Member Zawierająca składowa diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index ea9f9a89fb718..86052411d481c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -287,11 +287,6 @@ Comentários - - Compute Quick Actions asynchronously (experimental, requires restart) - Calcular Ações Rápidas de forma assíncrona (experimental, requer reinicialização) - - Containing Member Contendo Membro diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 6eabee3abed9f..e9ac7c09832bd 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -287,11 +287,6 @@ Комментарии - - Compute Quick Actions asynchronously (experimental, requires restart) - Асинхронное вычисление быстрых действий (экспериментальная функция, требуется перезапуск) - - Containing Member Содержащий член diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 8707521d0f521..b4c840d539e78 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -287,11 +287,6 @@ Açıklamalar - - Compute Quick Actions asynchronously (experimental, requires restart) - Hızlı Eylemleri zaman uyumsuz şekilde hesapla (deneysel, yeniden başlatma gerektirir) - - Containing Member Kapsayan Üye @@ -434,7 +429,7 @@ Enable document outline (experimental, requires restart) - Enable document outline (experimental, requires restart) + Belge ana hattını etkinleştir (deneysel, yeniden başlatma gerektirir) @@ -1499,7 +1494,7 @@ Sync namespaces changes - Sync namespaces changes + Ad alanı değişikliklerini eşitle diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 1295719eeb24a..54b8e370c623d 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -287,11 +287,6 @@ 备注 - - Compute Quick Actions asynchronously (experimental, requires restart) - 异步计算快速操作(实验性,需要重启) - - Containing Member 包含成员 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index be306dde0dd0b..d6195861cd0fd 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -287,11 +287,6 @@ 註解 - - Compute Quick Actions asynchronously (experimental, requires restart) - 非同步計算快速動作 (實驗性,需要重新開機) - - Containing Member 包含的成員 diff --git a/src/VisualStudio/Core/Test/CodeModel/CSharp/EventCollectorTests.vb b/src/VisualStudio/Core/Test/CodeModel/CSharp/EventCollectorTests.vb index 8d52c77eacebe..8235c8129b3e8 100644 --- a/src/VisualStudio/Core/Test/CodeModel/CSharp/EventCollectorTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/CSharp/EventCollectorTests.vb @@ -905,7 +905,7 @@ class Program - Public Async Function DontFireEventForMethodAddedInsideNamespace() As Task + Public Async Function DoNotFireEventForMethodAddedInsideNamespace() As Task Dim code = namespace N @@ -928,7 +928,7 @@ namespace N - Public Async Function DontCrashOnDuplicatedMethodsInNamespace() As Task + Public Async Function DoNotCrashOnDuplicatedMethodsInNamespace() As Task Dim code = namespace N @@ -958,7 +958,7 @@ namespace N - Public Async Function DontCrashOnDuplicatedPropertiesInNamespace() As Task + Public Async Function DoNotCrashOnDuplicatedPropertiesInNamespace() As Task Dim code = namespace N @@ -981,7 +981,7 @@ namespace N - Public Async Function DontCrashOnDuplicatedEventsInNamespace1() As Task + Public Async Function DoNotCrashOnDuplicatedEventsInNamespace1() As Task Dim code = namespace N @@ -1004,7 +1004,7 @@ namespace N - Public Async Function DontCrashOnDuplicatedEventsInNamespace2() As Task + Public Async Function DoNotCrashOnDuplicatedEventsInNamespace2() As Task Dim code = namespace N diff --git a/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb b/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb index 484aa6bfbf86d..82162b25c9d9d 100644 --- a/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb +++ b/src/VisualStudio/Core/Test/CodeModel/MethodXML/MethodXMLTests_VBAssignments.vb @@ -877,7 +877,7 @@ End Class End Sub - Public Sub TestVBAssignments_DontThrowWhenLeftHandSideDoesntBind() + Public Sub TestVBAssignments_DoNotThrowWhenLeftHandSideDoesntBind() Dim definition = diff --git a/src/VisualStudio/Core/Test/CodeModel/VisualBasic/EventCollectorTests.vb b/src/VisualStudio/Core/Test/CodeModel/VisualBasic/EventCollectorTests.vb index 86640ba188b65..6bb99b99354d2 100644 --- a/src/VisualStudio/Core/Test/CodeModel/VisualBasic/EventCollectorTests.vb +++ b/src/VisualStudio/Core/Test/CodeModel/VisualBasic/EventCollectorTests.vb @@ -1975,7 +1975,7 @@ End Class - Public Async Function TestDontFireEventsForGarbage1() As Task + Public Async Function TestDoNotFireEventsForGarbage1() As Task Dim code = Class C @@ -2001,7 +2001,7 @@ End Class - Public Async Function TestDontFireEventsForGarbage2() As Task + Public Async Function TestDoNotFireEventsForGarbage2() As Task Dim code = Partial Class SomeClass @@ -2265,7 +2265,7 @@ End Class - Public Async Function DontCrashOnDuplicatedMethodsInNamespace() As Task + Public Async Function DoNotCrashOnDuplicatedMethodsInNamespace() As Task Dim code = Namespace N @@ -2290,7 +2290,7 @@ End Namespace - Public Async Function DontCrashOnDuplicatedPropertiesInNamespace() As Task + Public Async Function DoNotCrashOnDuplicatedPropertiesInNamespace() As Task Dim code = Namespace N @@ -2311,7 +2311,7 @@ End Namespace - Public Async Function DontCrashOnDuplicatedEventsInNamespace1() As Task + Public Async Function DoNotCrashOnDuplicatedEventsInNamespace1() As Task Dim code = Namespace N @@ -2332,7 +2332,7 @@ End Namespace - Public Async Function DontCrashOnDuplicatedEventsInNamespace2() As Task + Public Async Function DoNotCrashOnDuplicatedEventsInNamespace2() As Task Dim code = Namespace N diff --git a/src/VisualStudio/Core/Test/CommonControls/MemberSelectionViewModelTests.vb b/src/VisualStudio/Core/Test/CommonControls/MemberSelectionViewModelTests.vb index 9ffe4cee3908c..c7532cc0ecf32 100644 --- a/src/VisualStudio/Core/Test/CommonControls/MemberSelectionViewModelTests.vb +++ b/src/VisualStudio/Core/Test/CommonControls/MemberSelectionViewModelTests.vb @@ -61,7 +61,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.CommonControls End Function - Public Async Function TestMemberSelectionViewModelDont_PullDisableItem() As Task + Public Async Function TestMemberSelectionViewModelDoNot_PullDisableItem() As Task Dim markUp = - Public Async Function TestGenerateType_DontGenerateIntoExistingFile() As Task + Public Async Function TestGenerateType_DoNotGenerateIntoExistingFile() As Task ' Get a Temp Folder Path Dim projectRootFolder = Path.GetTempPath() diff --git a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index 72ab0fc46b145..b1da37d12579f 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -238,7 +238,9 @@ class { } diagnosticService.CreateIncrementalAnalyzer(workspace) ' confirm that IDE doesn't report the diagnostics - Dim diagnostics = Await diagnosticService.GetDiagnosticsAsync(workspace.CurrentSolution, documentId:=document.Id) + Dim diagnostics = Await diagnosticService.GetDiagnosticsAsync(workspace.CurrentSolution, projectId:=Nothing, documentId:=document.Id, + includeSuppressedDiagnostics:=False, includeNonLocalDocumentDiagnostics:=True, + CancellationToken.None) Assert.False(diagnostics.Any()) End Using End Function diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index 2cf06a37d1738..b6134d3033937 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -78,6 +78,12 @@ true BindingRedirect + + Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler + BuiltProjectOutputGroup + true + BindingRedirect + Microsoft.CodeAnalysis.ExternalAccess.Xamarin.Remote BuiltProjectOutputGroup diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml index 9c5f6c7683f2e..6975166c51410 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml @@ -111,15 +111,7 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Add_missing_imports_on_paste}" /> - - - - - - - + diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index a9c47431824aa..a9b75cd6c3bef 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -93,13 +93,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(SuggestForTypesInNuGetPackages, SymbolSearchOptionsStorage.SearchNuGetPackages, LanguageNames.VisualBasic) BindToOption(AddMissingImportsOnPaste, AddImportOnPasteOptionsStorage.AddImportsOnPaste, LanguageNames.VisualBasic) - ' Quick Actions - BindToOption(ComputeQuickActionsAsynchronouslyExperimental, SuggestionsOptionsStorage.Asynchronous, - Function() - ' If the option has Not been set by the user, check if the option is disabled from experimentation. - Return Not optionStore.GetOption(SuggestionsOptionsStorage.AsynchronousQuickActionsDisableFeatureFlag) - End Function) - ' Highlighting BindToOption(EnableHighlightReferences, ReferenceHighlightingOptionsStorage.ReferenceHighlighting, LanguageNames.VisualBasic) BindToOption(EnableHighlightKeywords, KeywordHighlightingOptionsStorage.KeywordHighlighting, LanguageNames.VisualBasic) @@ -158,7 +151,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(Editor_color_scheme, ColorSchemeOptionsStorage.ColorScheme) ' Extract method - BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptionsStorage.DontPutOutOrRefOnStruct, LanguageNames.VisualBasic) + BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptionsStorage.DoNotPutOutOrRefOnStruct, LanguageNames.VisualBasic) ' Implement Interface or Abstract Class BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptionsStorage.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.VisualBasic) diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb index 6c2761c8946db..9f3e7046a08e4 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageStrings.vb @@ -145,9 +145,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Public ReadOnly Property Option_Quick_Actions As String = ServicesVSResources.Quick_Actions - Public ReadOnly Property Option_Compute_Quick_Actions_asynchronously_experimental As String = - ServicesVSResources.Compute_Quick_Actions_asynchronously_experimental - Public ReadOnly Property Option_EnableOutlining As String = BasicVSResources.Enter_outlining_mode_when_files_open diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.vb b/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.vb index 4cf5d8df43a0a..4f3631cc2a832 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.ExtractMethod.vb @@ -17,10 +17,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Public Property ExtractMethod_DoNotPutOutOrRefOnStruct As Boolean Get - Return GetBooleanOption(ExtractMethodOptionsStorage.DontPutOutOrRefOnStruct) + Return GetBooleanOption(ExtractMethodOptionsStorage.DoNotPutOutOrRefOnStruct) End Get Set(value As Boolean) - SetBooleanOption(ExtractMethodOptionsStorage.DontPutOutOrRefOnStruct, value) + SetBooleanOption(ExtractMethodOptionsStorage.DoNotPutOutOrRefOnStruct, value) End Set End Property End Class diff --git a/src/Workspaces/CSharp/Portable/Classification/CSharpClassificationService.cs b/src/Workspaces/CSharp/Portable/Classification/CSharpClassificationService.cs index a0bb3b2c1e896..3c3ca807656af 100644 --- a/src/Workspaces/CSharp/Portable/Classification/CSharpClassificationService.cs +++ b/src/Workspaces/CSharp/Portable/Classification/CSharpClassificationService.cs @@ -6,8 +6,8 @@ using System.Composition; using System.Threading; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Classification @@ -21,7 +21,7 @@ public CSharpEditorClassificationService() { } - public override void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public override void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) => ClassificationHelpers.AddLexicalClassifications(text, textSpan, result, cancellationToken); public override ClassifiedSpan AdjustStaleClassification(SourceText text, ClassifiedSpan classifiedSpan) diff --git a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs index 8e81a4aa37b98..d87c716ad094a 100644 --- a/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs +++ b/src/Workspaces/CSharp/Portable/Classification/ClassificationHelpers.cs @@ -4,9 +4,9 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Classification @@ -497,7 +497,7 @@ private static bool IsActualContextualKeyword(SyntaxToken token) return false; } - internal static void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + internal static void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { var text2 = text.ToString(textSpan); var tokens = SyntaxFactory.ParseTokens(text2, initialTokenPosition: textSpan.Start); diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpSyntaxClassificationService.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpSyntaxClassificationService.cs index e6369819fd6cb..3daebfc7f3ce4 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpSyntaxClassificationService.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/CSharpSyntaxClassificationService.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Classification.Classifiers; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; @@ -34,10 +35,10 @@ public CSharpSyntaxClassificationService() public override ImmutableArray GetDefaultSyntaxClassifiers() => s_defaultSyntaxClassifiers; - public override void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public override void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) => ClassificationHelpers.AddLexicalClassifications(text, textSpan, result, cancellationToken); - public override void AddSyntacticClassifications(SyntaxNode root, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public override void AddSyntacticClassifications(SyntaxNode root, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) => Worker.CollectClassifiedSpans(root, textSpan, result, cancellationToken); public override ClassifiedSpan FixClassification(SourceText rawText, ClassifiedSpan classifiedSpan) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/DiscardSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/DiscardSyntaxClassifier.cs index 482bc526dc8fa..74eb1f2ac56d1 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/DiscardSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/DiscardSyntaxClassifier.cs @@ -7,8 +7,8 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Classification.Classifiers { @@ -24,7 +24,8 @@ internal class DiscardSyntaxClassifier : AbstractSyntaxClassifier SyntaxNode syntax, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, CancellationToken cancellationToken) + SegmentedList result, + CancellationToken cancellationToken) { if (syntax.IsKind(SyntaxKind.DiscardDesignation) || syntax.IsKind(SyntaxKind.DiscardPattern)) { diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 1f5269be22c56..792d389c8bc04 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -9,6 +9,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -23,7 +24,7 @@ internal class NameSyntaxClassifier : AbstractNameSyntaxClassifier SyntaxNode syntax, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { if (syntax is NameSyntax name) @@ -50,7 +51,7 @@ protected override bool IsParentAnAttribute(SyntaxNode node) private void ClassifyTypeSyntax( NameSyntax name, SemanticModel semanticModel, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { var symbolInfo = semanticModel.GetSymbolInfo(name, cancellationToken); @@ -67,7 +68,7 @@ protected override bool IsParentAnAttribute(SyntaxNode node) NameSyntax name, SymbolInfo symbolInfo, SemanticModel semanticModel, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { if (symbolInfo.CandidateReason is @@ -100,7 +101,7 @@ CandidateReason.Ambiguous or NameSyntax name, SymbolInfo symbolInfo, SemanticModel semanticModel, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { // If everything classifies the same way, then just pick that classification. @@ -312,7 +313,7 @@ private static bool IsInVarContext(NameSyntax name) private static bool TryClassifyFromIdentifier( NameSyntax name, SymbolInfo symbolInfo, - ArrayBuilder result) + SegmentedList result) { // Okay - it wasn't a type. If the syntax matches "var q = from" or "q = from", and from // doesn't bind to anything then optimistically color from as a keyword. @@ -334,7 +335,7 @@ private static bool IsInVarContext(NameSyntax name) private static bool TryClassifyValueIdentifier( NameSyntax name, SymbolInfo symbolInfo, - ArrayBuilder result) + SegmentedList result) { if (name is IdentifierNameSyntax identifierName && symbolInfo.Symbol.IsImplicitValueParameter()) @@ -347,7 +348,7 @@ private static bool IsInVarContext(NameSyntax name) } private static bool TryClassifyNameOfIdentifier( - NameSyntax name, SymbolInfo symbolInfo, ArrayBuilder result) + NameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { if (name is IdentifierNameSyntax identifierName && identifierName.Identifier.IsKindOrHasMatchingText(SyntaxKind.NameOfKeyword) && @@ -360,7 +361,7 @@ private static bool IsInVarContext(NameSyntax name) return false; } - private static bool TryClassifyAsyncIdentifier(NameSyntax name, SymbolInfo symbolInfo, ArrayBuilder result) + private static bool TryClassifyAsyncIdentifier(NameSyntax name, SymbolInfo symbolInfo, SegmentedList result) { var symbol = symbolInfo.GetAnySymbol(); diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.cs index 4e8b13950c7ff..fa69bf13a8a39 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.cs @@ -7,8 +7,8 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Classification @@ -25,7 +25,7 @@ internal class OperatorOverloadSyntaxClassifier : AbstractSyntaxClassifier SyntaxNode syntax, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { var symbolInfo = semanticModel.GetSymbolInfo(syntax, cancellationToken); diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/SyntaxTokenClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/SyntaxTokenClassifier.cs index 247e90a093cec..ba2bcc9e00ced 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/SyntaxTokenClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/SyntaxTokenClassifier.cs @@ -8,6 +8,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -26,7 +27,8 @@ internal class SyntaxTokenClassifier : AbstractSyntaxClassifier SyntaxToken lessThanToken, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, CancellationToken cancellationToken) + SegmentedList result, + CancellationToken cancellationToken) { var syntaxTree = semanticModel.SyntaxTree; if (syntaxTree.IsInPartiallyWrittenGeneric(lessThanToken.Span.End, cancellationToken, out var identifier)) diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/UsingDirectiveSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/UsingDirectiveSyntaxClassifier.cs index 345f67edc9092..09bad04cf7425 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/UsingDirectiveSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/UsingDirectiveSyntaxClassifier.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -18,7 +19,7 @@ internal class UsingDirectiveSyntaxClassifier : AbstractSyntaxClassifier SyntaxNode syntax, SemanticModel semanticModel, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { if (syntax is UsingDirectiveSyntax usingDirective) @@ -32,7 +33,7 @@ internal class UsingDirectiveSyntaxClassifier : AbstractSyntaxClassifier private static void ClassifyUsingDirectiveSyntax( UsingDirectiveSyntax usingDirective, SemanticModel semanticModel, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { // For using aliases, we bind the target on the right of the equals and use that diff --git a/src/Workspaces/CSharp/Portable/Classification/Worker.cs b/src/Workspaces/CSharp/Portable/Classification/Worker.cs index 018ea749dfa9e..b3103da72eb8d 100644 --- a/src/Workspaces/CSharp/Portable/Classification/Worker.cs +++ b/src/Workspaces/CSharp/Portable/Classification/Worker.cs @@ -7,7 +7,7 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -23,10 +23,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Classification internal readonly ref partial struct Worker { private readonly TextSpan _textSpan; - private readonly ArrayBuilder _result; + private readonly SegmentedList _result; private readonly CancellationToken _cancellationToken; - private Worker(TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + private Worker(TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { _result = result; _textSpan = textSpan; @@ -34,17 +34,15 @@ private Worker(TextSpan textSpan, ArrayBuilder result, Cancellat } internal static void CollectClassifiedSpans( - IEnumerable tokens, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + IEnumerable tokens, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { var worker = new Worker(textSpan, result, cancellationToken); foreach (var tk in tokens) - { worker.ClassifyToken(tk); - } } internal static void CollectClassifiedSpans( - SyntaxNode node, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + SyntaxNode node, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { var worker = new Worker(textSpan, result, cancellationToken); worker.ClassifyNode(node); diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 417904d737b70..01b4c3983462e 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -84,7 +84,7 @@ internal override SyntaxNode DocumentationCommentTrivia(IEnumerable { var docTrivia = SyntaxFactory.DocumentationCommentTrivia( SyntaxKind.MultiLineDocumentationCommentTrivia, - SyntaxFactory.List(nodes), + (SyntaxList)SyntaxFactory.List(nodes), SyntaxFactory.Token(SyntaxKind.EndOfDocumentationCommentToken)); docTrivia = docTrivia.WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("/// ")) @@ -97,7 +97,7 @@ internal override SyntaxNode DocumentationCommentTrivia(IEnumerable { if (trivia.GetStructure() is DocumentationCommentTriviaSyntax documentationCommentTrivia) { - return SyntaxFactory.DocumentationCommentTrivia(documentationCommentTrivia.Kind(), SyntaxFactory.List(content), documentationCommentTrivia.EndOfComment); + return SyntaxFactory.DocumentationCommentTrivia(documentationCommentTrivia.Kind(), (SyntaxList)SyntaxFactory.List(content), documentationCommentTrivia.EndOfComment); } return null; @@ -3172,7 +3172,7 @@ public override SyntaxNode MemberBindingExpression(SyntaxNode name) public override SyntaxNode ElementBindingExpression(IEnumerable arguments) => SyntaxFactory.ElementBindingExpression( - SyntaxFactory.BracketedArgumentList(SyntaxFactory.SeparatedList(arguments))); + SyntaxFactory.BracketedArgumentList((SeparatedSyntaxList)SyntaxFactory.SeparatedList(arguments))); /// /// Parenthesize the left hand size of a member access, invocation or element access expression @@ -3217,7 +3217,7 @@ public override SyntaxNode ObjectCreationExpression(SyntaxNode type, IEnumerable internal override SyntaxNode ObjectCreationExpression(SyntaxNode type, SyntaxToken openParen, SeparatedSyntaxList arguments, SyntaxToken closeParen) => SyntaxFactory.ObjectCreationExpression( (TypeSyntax)type, - SyntaxFactory.ArgumentList(openParen, arguments, closeParen), + SyntaxFactory.ArgumentList(openParen, (SeparatedSyntaxList)arguments, closeParen), initializer: null); private static ArgumentListSyntax CreateArgumentList(IEnumerable arguments) diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index 576302c589631..3ffa9836ba44e 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis.CSharp true - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 full @@ -35,8 +35,10 @@ + + diff --git a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs index d7f81c58a2775..4ee027d2c22e7 100644 --- a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs +++ b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs @@ -192,9 +192,7 @@ private RecommendedSymbols GetSymbolsOffOfRangeExpression(RangeExpressionSyntax private ImmutableArray GetSymbolsForGlobalStatementContext() { - var syntaxTree = _context.SyntaxTree; - var position = _context.Position; - var token = _context.LeftToken; + var token = _context.TargetToken; // The following code is a hack to get around a binding problem when asking binding // questions immediately after a using directive. This is special-cased in the binder @@ -205,16 +203,8 @@ private ImmutableArray GetSymbolsForGlobalStatementContext() // using System; // | - if (token.Kind() == SyntaxKind.SemicolonToken && - token.Parent.IsKind(SyntaxKind.UsingDirective) && - position >= token.Span.End) - { - var compUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(_cancellationToken); - if (compUnit.Usings.Count > 0 && compUnit.Usings.Last().SemicolonToken == token) - { - token = token.GetNextToken(includeZeroWidth: true); - } - } + if (_context.IsRightAfterUsingOrImportDirective) + token = token.GetNextToken(includeZeroWidth: true); var symbols = _context.SemanticModel.LookupSymbols(token.SpanStart); diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.Expander.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.Expander.cs index 07d42ed671dd5..7ff18d4e955fb 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.Expander.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.Expander.cs @@ -594,7 +594,7 @@ SyntaxToken GetNewIdentifier(SyntaxToken _identifier) // if the user already used the Attribute suffix in the attribute, we'll maintain it. if (identifier.ValueText == name && name.EndsWith("Attribute", StringComparison.Ordinal)) { - identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation); + identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation); } identifier = identifier.CopyAnnotationsTo(SyntaxFactory.VerbatimIdentifier(identifier.LeadingTrivia, name, name, identifier.TrailingTrivia)); diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.NodesAndTokensToReduceComputer.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.NodesAndTokensToReduceComputer.cs index 2c88de51e45e5..6118e17108b48 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.NodesAndTokensToReduceComputer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.NodesAndTokensToReduceComputer.cs @@ -54,8 +54,8 @@ public override SyntaxNode Visit(SyntaxNode node) if (_simplifyAllDescendants) { // One of the ancestor node is within a simplification span, but this node is outside all simplification spans. - // Add DontSimplifyAnnotation to node to ensure it doesn't get simplified. - return node.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation); + // Add DoNotSimplifyAnnotation to node to ensure it doesn't get simplified. + return node.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation); } else { @@ -92,8 +92,8 @@ public override SyntaxToken VisitToken(SyntaxToken token) if (_simplifyAllDescendants) { // One of the ancestor node is within a simplification span, but this token is outside all simplification spans. - // Add DontSimplifyAnnotation to token to ensure it doesn't get simplified. - return token.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation); + // Add DoNotSimplifyAnnotation to token to ensure it doesn't get simplified. + return token.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation); } else { diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs index f552d8db94158..882edf733e694 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs @@ -10,6 +10,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; @@ -67,28 +68,31 @@ public void RequireInitialized() Contract.ThrowIfNull(SemanticModel); } - private static SyntaxNode? GetParentNode(SyntaxNode node) + private static SyntaxNode GetParentNode(SyntaxNode node) => node switch { ExpressionSyntax expression => GetParentNode(expression), PatternSyntax pattern => GetParentNode(pattern), CrefSyntax cref => GetParentNode(cref), - _ => null + _ => node.GetRequiredParent(), }; private static SyntaxNode GetParentNode(ExpressionSyntax expression) { - var lastExpression = expression; - for (SyntaxNode? current = expression; current != null; current = current.Parent) + // Walk all the way up the expression to the non-expression parent. Effectively, once we change an + // expression *within* some larger expression context, we want to stop rewriting any further sibling + // expressions as they could be affected by this change. + + SyntaxNode parent = expression; + for (var current = (SyntaxNode)expression; current != null; current = current.Parent) { - if (current is ExpressionSyntax currentExpression) - { - lastExpression = currentExpression; - } + // if we're in an argument, walk up into that as well as the change in one argument can affect + // other arguments in a call. + if (current is ExpressionSyntax or ArgumentSyntax) + parent = current; } - Contract.ThrowIfNull(lastExpression.Parent); - return lastExpression.Parent; + return parent.GetRequiredParent(); } private static SyntaxNode GetParentNode(PatternSyntax pattern) @@ -117,13 +121,13 @@ private static SyntaxNode GetParentNode(CrefSyntax cref) return topMostCref.Parent; } - protected SyntaxNode SimplifyNode( + protected SyntaxNode? SimplifyNode( TNode node, - SyntaxNode newNode, - SyntaxNode parentNode, + SyntaxNode? newNode, Func simplifier) where TNode : SyntaxNode { + var parentNode = GetParentNode(node); RequireInitialized(); this.CancellationToken.ThrowIfCancellationRequested(); @@ -139,7 +143,7 @@ private static SyntaxNode GetParentNode(CrefSyntax cref) return newNode; } - if (!node.HasAnnotation(SimplificationHelpers.DontSimplifyAnnotation)) + if (!node.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation)) { var simplifiedNode = simplifier(node, this.SemanticModel, this.Options, this.CancellationToken); if (simplifiedNode != node) @@ -153,19 +157,6 @@ private static SyntaxNode GetParentNode(CrefSyntax cref) return node; } - protected SyntaxNode SimplifyExpression( - TExpression expression, - SyntaxNode newNode, - Func simplifier) - where TExpression : SyntaxNode - { - var parentNode = GetParentNode(expression); - if (parentNode == null) - return newNode; - - return SimplifyNode(expression, newNode, parentNode, simplifier); - } - protected SyntaxToken SimplifyToken(SyntaxToken token, Func simplifier) { RequireInitialized(); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs index 0e1feeb94a0a2..cf92e7867f5ac 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpCastReducer.Rewriter.cs @@ -22,7 +22,7 @@ public Rewriter(ObjectPool pool) public override SyntaxNode VisitCastExpression(CastExpressionSyntax node) { - return SimplifyExpression( + return SimplifyNode( node, newNode: base.VisitCastExpression(node), simplifier: s_simplifyCast); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.Rewriter.cs index 5eaf06750c7fa..73fc710a70cf9 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.Rewriter.cs @@ -45,13 +45,10 @@ public Rewriter(ObjectPool pool) } public override SyntaxNode VisitDefaultExpression(DefaultExpressionSyntax node) - { - return SimplifyNode( + => SimplifyNode( node, newNode: base.VisitDefaultExpression(node), - parentNode: node.Parent, simplifier: _simplifyDefaultExpression); - } } } } diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.cs index 8246972fde934..7771b9d0a2be0 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpDefaultExpressionReducer.cs @@ -2,16 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Simplification { internal partial class CSharpDefaultExpressionReducer : AbstractCSharpReducer { - private static readonly ObjectPool s_pool = new( - () => new Rewriter(s_pool)); + private static readonly ObjectPool s_pool = new(static () => new Rewriter(s_pool)); public CSharpDefaultExpressionReducer() : base(s_pool) { diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs index 97be79eebc13d..6e54bdd4d5e08 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpExtensionMethodReducer.Rewriter.cs @@ -20,7 +20,7 @@ public Rewriter(ObjectPool pool) public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node) { - return SimplifyExpression( + return SimplifyNode( node, newNode: base.VisitInvocationExpression(node), simplifier: s_simplifyExtensionMethod); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs index 083b2604f1521..cd2c393828191 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpInferredMemberNameReducer.Rewriter.cs @@ -7,7 +7,6 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Simplification; @@ -58,7 +57,6 @@ public override SyntaxNode VisitArgument(ArgumentSyntax node) { return SimplifyNode( node, - parentNode: node.Parent, newNode: newNode, simplifier: s_simplifyTupleName); } @@ -67,13 +65,10 @@ public override SyntaxNode VisitArgument(ArgumentSyntax node) } public override SyntaxNode VisitAnonymousObjectMemberDeclarator(AnonymousObjectMemberDeclaratorSyntax node) - { - return SimplifyNode( + => SimplifyNode( node, - parentNode: node.Parent, newNode: base.VisitAnonymousObjectMemberDeclarator(node), simplifier: s_simplifyAnonymousTypeMemberName); - } } } } diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs index 9b71df0d4a4d8..3ff1ea33fb204 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpMiscellaneousReducer.Rewriter.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 Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -18,32 +16,23 @@ public Rewriter(ObjectPool pool) { } - public override SyntaxNode VisitParameter(ParameterSyntax node) - { - return SimplifyNode( + public override SyntaxNode? VisitParameter(ParameterSyntax node) + => SimplifyNode( node, newNode: base.VisitParameter(node), - parentNode: node.Parent, simplifier: s_simplifyParameter); - } - public override SyntaxNode VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node) - { - return SimplifyNode( + public override SyntaxNode? VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node) + => SimplifyNode( node, newNode: base.VisitParenthesizedLambdaExpression(node), - parentNode: node.Parent, simplifier: s_simplifyParenthesizedLambdaExpression); - } - public override SyntaxNode VisitBlock(BlockSyntax node) - { - return SimplifyNode( + public override SyntaxNode? VisitBlock(BlockSyntax node) + => SimplifyNode( node, newNode: base.VisitBlock(node), - parentNode: node.Parent, simplifier: s_simplifyBlock); - } } } } diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs index 97cc32e70b155..9372a6630a503 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNameReducer.Rewriter.cs @@ -29,7 +29,7 @@ public override SyntaxNode VisitPredefinedType(PredefinedTypeSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitPredefinedType(node), simplifier: s_simplifyName); @@ -47,7 +47,7 @@ public override SyntaxNode VisitAliasQualifiedName(AliasQualifiedNameSyntax node this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitAliasQualifiedName(node), simplifier: s_simplifyName); @@ -65,7 +65,7 @@ public override SyntaxNode VisitQualifiedName(QualifiedNameSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitQualifiedName(node), simplifier: s_simplifyName); @@ -83,7 +83,7 @@ public override SyntaxNode VisitMemberAccessExpression(MemberAccessExpressionSyn this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitMemberAccessExpression(node), simplifier: s_simplifyName); @@ -101,7 +101,7 @@ public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitIdentifierName(node), simplifier: s_simplifyName); @@ -119,7 +119,7 @@ public override SyntaxNode VisitGenericName(GenericNameSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitGenericName(node), simplifier: s_simplifyName); @@ -137,7 +137,7 @@ public override SyntaxNode VisitQualifiedCref(QualifiedCrefSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitQualifiedCref(node), simplifier: s_simplifyName); @@ -155,7 +155,7 @@ public override SyntaxNode VisitArrayType(ArrayTypeSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitArrayType(node), simplifier: s_simplifyName); @@ -173,7 +173,7 @@ public override SyntaxNode VisitNullableType(NullableTypeSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitNullableType(node), simplifier: s_simplifyName); @@ -191,7 +191,7 @@ public override SyntaxNode VisitTupleType(TupleTypeSyntax node) this.alwaysSimplify = node.HasAnnotation(Simplifier.Annotation); } - var result = SimplifyExpression( + var result = SimplifyNode( node, newNode: base.VisitTupleType(node), simplifier: s_simplifyName); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNullableAnnotationReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNullableAnnotationReducer.Rewriter.cs index 0589b2adca76f..1b8856325ac8d 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNullableAnnotationReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpNullableAnnotationReducer.Rewriter.cs @@ -19,7 +19,7 @@ public Rewriter(ObjectPool pool) : base(pool) public override SyntaxNode VisitNullableType(NullableTypeSyntax node) { - return SimplifyExpression( + return SimplifyNode( node, base.VisitNullableType(node), simplifier: s_simplifyNullableType); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedExpressionReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedExpressionReducer.Rewriter.cs index 9ea73629f4815..d570b35e1a487 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedExpressionReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedExpressionReducer.Rewriter.cs @@ -20,7 +20,7 @@ public Rewriter(ObjectPool pool) public override SyntaxNode VisitParenthesizedExpression(ParenthesizedExpressionSyntax node) { - return SimplifyExpression( + return SimplifyNode( node, newNode: base.VisitParenthesizedExpression(node), simplifier: s_simplifyParentheses); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedPatternReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedPatternReducer.Rewriter.cs index bfeb103868fe3..cfb6c0b052cf4 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedPatternReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpParenthesizedPatternReducer.Rewriter.cs @@ -20,7 +20,7 @@ public Rewriter(ObjectPool pool) public override SyntaxNode VisitParenthesizedPattern(ParenthesizedPatternSyntax node) { - return SimplifyExpression( + return SimplifyNode( node, newNode: base.VisitParenthesizedPattern(node), simplifier: s_simplifyParentheses); diff --git a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs index c3c14d905664e..74c1ed912b660 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Reducers/CSharpVarReducer.Rewriter.cs @@ -31,7 +31,7 @@ private SyntaxNode ProcessTypeSyntax(TypeSyntax typeSyntax) } // Definitely do not simplify us if we were told to not simplify. - if (typeSyntax.HasAnnotation(SimplificationHelpers.DontSimplifyAnnotation)) + if (typeSyntax.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation)) { return typeSyntax; } diff --git a/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/NameSimplifier.cs b/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/NameSimplifier.cs index 50d6aad39a5fd..b83755484e674 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/NameSimplifier.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/Simplifiers/NameSimplifier.cs @@ -487,8 +487,8 @@ private static bool CanSimplifyNullable(INamedTypeSymbol type, NameSyntax name, { const string AttributeName = "Attribute"; - // an attribute that should keep it (unnecessary "Attribute" suffix should be annotated with a DontSimplifyAnnotation - if (identifierToken.ValueText != AttributeName && identifierToken.ValueText.EndsWith(AttributeName, StringComparison.Ordinal) && !identifierToken.HasAnnotation(SimplificationHelpers.DontSimplifyAnnotation)) + // an attribute that should keep it (unnecessary "Attribute" suffix should be annotated with a DoNotSimplifyAnnotation + if (identifierToken.ValueText != AttributeName && identifierToken.ValueText.EndsWith(AttributeName, StringComparison.Ordinal) && !identifierToken.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation)) { // weird. the semantic model is able to bind attribute syntax like "[as()]" although it's not valid code. // so we need another check for keywords manually. diff --git a/src/Workspaces/CSharpTest/CodeGeneration/AddImportsTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/AddImportsTests.cs index 7d22a453aacdf..9cd999c55dc1d 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/AddImportsTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/AddImportsTests.cs @@ -172,7 +172,7 @@ class C } [Theory, MemberData(nameof(TestAllData))] - public async Task TestDontAddSystemImportFirst(bool useSymbolAnnotations) + public async Task TestDoNotAddSystemImportFirst(bool useSymbolAnnotations) { await TestAsync( @"using N; diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs index d7a61dd5f8e42..36b2508ef0bec 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs @@ -35,7 +35,7 @@ public async Task Simple1() => await AssertFormatAsync("namespace A/*1*/{}/*2*/ class A {}", "namespace A{ } class A {}"); [Fact] - public async Task DontFormatTriviaOutsideOfSpan_IncludingTrailingTriviaOnNewLine() + public async Task DoNotFormatTriviaOutsideOfSpan_IncludingTrailingTriviaOnNewLine() { var content = @"namespace A /*1*/{ diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index e2ba12dc55ced..45c7d74ef485b 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -2308,7 +2308,7 @@ public async Task Enums_Bug2586() } [Fact] - public async Task DontInsertLineBreaksInSingleLineEnum() + public async Task DoNotInsertLineBreaksInSingleLineEnum() => await AssertFormatAsync(@"enum E { a = 10, b, c }", @"enum E { a = 10, b, c }"); [Fact] @@ -2602,7 +2602,7 @@ void M() } [Fact] - public async Task DontAddLineBreakBeforeWhere1_Bug2582() + public async Task DoNotAddLineBreakBeforeWhere1_Bug2582() { await AssertFormatAsync(@"class C { @@ -2618,7 +2618,7 @@ public async Task DontAddLineBreakBeforeWhere1_Bug2582() } [Fact] - public async Task DontAddLineBreakBeforeWhere2_Bug2582() + public async Task DoNotAddLineBreakBeforeWhere2_Bug2582() { await AssertFormatAsync(@"class C where T : I { @@ -2628,7 +2628,7 @@ public async Task DontAddLineBreakBeforeWhere2_Bug2582() } [Fact] - public async Task DontAddSpaceAfterUnaryMinus() + public async Task DoNotAddSpaceAfterUnaryMinus() { await AssertFormatAsync(@"class C { @@ -2646,7 +2646,7 @@ void M() } [Fact] - public async Task DontAddSpaceAfterUnaryPlus() + public async Task DoNotAddSpaceAfterUnaryPlus() { await AssertFormatAsync(@"class C { @@ -2664,7 +2664,7 @@ void M() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DontAddSpaceAfterIncrement() + public async Task DoNotAddSpaceAfterIncrement() { var code = @"class C { @@ -2677,7 +2677,7 @@ void M(int[] i) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DontAddSpaceBeforeIncrement() + public async Task DoNotAddSpaceBeforeIncrement() { var code = @"class C { @@ -2690,7 +2690,7 @@ void M(int[] i) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DontAddSpaceAfterDecrement() + public async Task DoNotAddSpaceAfterDecrement() { var code = @"class C { @@ -2703,7 +2703,7 @@ void M(int[] i) } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DontAddSpaceBeforeDecrement() + public async Task DoNotAddSpaceBeforeDecrement() { var code = @"class C { @@ -7409,7 +7409,7 @@ void M() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/991547")] - public async Task DontWrappingTryCatchFinallyIfOnSingleLine() + public async Task DoNotWrappingTryCatchFinallyIfOnSingleLine() { var code = @" class C @@ -8105,7 +8105,7 @@ static void Main(string[] args) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1118")] - public void DontAssumeCertainNodeAreAlwaysParented() + public void DoNotAssumeCertainNodeAreAlwaysParented() { var block = SyntaxFactory.Block(); Formatter.Format(block, new AdhocWorkspace().Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); @@ -8187,7 +8187,7 @@ public void goo() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1298")] - public async Task DontforceAccessorsToNewLineWithPropertyInitializers() + public async Task DoNotforceAccessorsToNewLineWithPropertyInitializers() { var code = @"using System.Collections.Generic; @@ -8216,7 +8216,7 @@ public class ExcludeValidation } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1339")] - public async Task DontFormatAutoPropertyInitializerIfNotDifferentLine() + public async Task DoNotFormatAutoPropertyInitializerIfNotDifferentLine() { var code = @"class Program { @@ -8396,7 +8396,7 @@ public async Task VerifySpacingAfterMethodDeclarationName_NonDefault() } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/939")] - public async Task DontFormatInsideArrayInitializers() + public async Task DoNotFormatInsideArrayInitializers() { var code = @"class Program { diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs index 71b1257504b81..b1159ead211fd 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs @@ -534,5 +534,133 @@ void MethodName(string value) await AssertFormatAsync(expected, content); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")] + public async Task FormatNestedListPattern1() + { + var content = """ + class C + { + void M(string[] ss) + { + if (ss is [ [ ] ]) + { + + } + } + } + """; + + var expected = """ + class C + { + void M(string[] ss) + { + if (ss is [[]]) + { + + } + } + } + """; + + await AssertFormatAsync(expected, content); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")] + public async Task FormatNestedListPattern2() + { + var content = """ + class C + { + void M(string[] ss) + { + if (ss is [ [ ],[ ] ]) + { + + } + } + } + """; + + var expected = """ + class C + { + void M(string[] ss) + { + if (ss is [[], []]) + { + + } + } + } + """; + + await AssertFormatAsync(expected, content); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")] + public async Task FormatNestedListPattern3() + { + var content = """ + class C + { + void M(string[] ss) + { + if (ss is [ [ ],[ ] , [ ] ] ) + { + + } + } + } + """; + + var expected = """ + class C + { + void M(string[] ss) + { + if (ss is [[], [], []]) + { + + } + } + } + """; + + await AssertFormatAsync(expected, content); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42861")] + public async Task FormatNestedListPattern4() + { + var content = """ + class C + { + void M(string[][] ss) + { + if (ss is [ [ [ ] ] ] ) + { + + } + } + } + """; + + var expected = """ + class C + { + void M(string[][] ss) + { + if (ss is [[[]]]) + { + + } + } + } + """; + + await AssertFormatAsync(expected, content); + } } } diff --git a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj index 0dc0700b7000d..075262492f489 100644 --- a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj +++ b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis true - net6.0;net472 + $(SourceBuildTargetFrameworks);net472 $(DefineConstants);WORKSPACE_MSBUILD true diff --git a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs index deca5d0ae04d4..8542ecefbd0d7 100644 --- a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; @@ -20,17 +21,17 @@ namespace Microsoft.CodeAnalysis.Classification { internal abstract class AbstractClassificationService : IClassificationService { - public abstract void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken); + public abstract void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); public abstract ClassifiedSpan AdjustStaleClassification(SourceText text, ClassifiedSpan classifiedSpan); public Task AddSemanticClassificationsAsync( - Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { return AddClassificationsAsync(document, textSpan, options, ClassificationType.Semantic, result, cancellationToken); } public Task AddEmbeddedLanguageClassificationsAsync( - Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { return AddClassificationsAsync(document, textSpan, options, ClassificationType.EmbeddedLanguage, result, cancellationToken); } @@ -40,7 +41,7 @@ internal abstract class AbstractClassificationService : IClassificationService TextSpan textSpan, ClassificationOptions options, ClassificationType type, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { var classificationService = document.GetLanguageService(); @@ -114,7 +115,7 @@ private static bool IsFullyLoaded(Document document, CancellationToken cancellat ClassificationType type, RemoteHostClient client, bool isFullyLoaded, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { // Only try to get cached classifications if we're not fully loaded yet. @@ -143,7 +144,7 @@ private static bool IsFullyLoaded(Document document, CancellationToken cancellat TextSpan textSpan, ClassificationType type, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { if (type == ClassificationType.Semantic) @@ -182,14 +183,14 @@ private static bool IsFullyLoaded(Document document, CancellationToken cancellat } } - public async Task AddSyntacticClassificationsAsync(Document document, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + public async Task AddSyntacticClassificationsAsync(Document document, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); AddSyntacticClassifications(document.Project.Solution.Services, root, textSpan, result, cancellationToken); } public void AddSyntacticClassifications( - SolutionServices services, SyntaxNode? root, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken) + SolutionServices services, SyntaxNode? root, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken) { if (root == null) return; diff --git a/src/Workspaces/Core/Portable/Classification/Classifier.cs b/src/Workspaces/Core/Portable/Classification/Classifier.cs index 4cca908776ef5..c4528b302b37c 100644 --- a/src/Workspaces/Core/Portable/Classification/Classifier.cs +++ b/src/Workspaces/Core/Portable/Classification/Classifier.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; @@ -20,6 +20,29 @@ namespace Microsoft.CodeAnalysis.Classification { public static class Classifier { + internal static PooledObject> GetPooledList(out SegmentedList classifiedSpans) + { + var pooledObject = new PooledObject>( + SharedPools.Default>(), + static p => + { + var result = p.Allocate(); + result.Clear(); + return result; + }, + static (p, list) => + { + // Deliberately do not call ClearAndFree for the set as we can easily have a set that goes past the + // threshold simply with a single classified screen. This allows reuse of those sets without causing + // lots of **garbage.** + list.Clear(); + p.Free(list); + }); + + classifiedSpans = pooledObject.Object; + return pooledObject; + } + public static async Task> GetClassifiedSpansAsync( Document document, TextSpan textSpan, @@ -68,8 +91,8 @@ public static class Classifier var getNodeClassifiers = extensionManager.CreateNodeExtensionGetter(syntaxClassifiers, c => c.SyntaxNodeTypes); var getTokenClassifiers = extensionManager.CreateTokenExtensionGetter(syntaxClassifiers, c => c.SyntaxTokenKinds); - using var _1 = ArrayBuilder.GetInstance(out var syntacticClassifications); - using var _2 = ArrayBuilder.GetInstance(out var semanticClassifications); + using var _1 = GetPooledList(out var syntacticClassifications); + using var _2 = GetPooledList(out var semanticClassifications); var root = semanticModel.SyntaxTree.GetRoot(cancellationToken); diff --git a/src/Workspaces/Core/Portable/Classification/ClassifierHelper.cs b/src/Workspaces/Core/Portable/Classification/ClassifierHelper.cs index bd7fbd99257c5..3d40942c2d6db 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassifierHelper.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassifierHelper.cs @@ -7,7 +7,7 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -42,8 +42,8 @@ internal static partial class ClassifierHelper // name), we'll do a later merging step to get the final correct list of // classifications. For tagging, normally the editor handles this. But as // we're producing the list of Inlines ourselves, we have to handles this here. - using var _1 = ArrayBuilder.GetInstance(out var syntaxSpans); - using var _2 = ArrayBuilder.GetInstance(out var semanticSpans); + using var _1 = Classifier.GetPooledList(out var syntaxSpans); + using var _2 = Classifier.GetPooledList(out var semanticSpans); await classificationService.AddSyntacticClassificationsAsync(document, span, syntaxSpans, cancellationToken).ConfigureAwait(false); @@ -68,7 +68,7 @@ internal static partial class ClassifierHelper return classifiedSpans; } - private static void RemoveAdditiveSpans(ArrayBuilder spans) + private static void RemoveAdditiveSpans(SegmentedList spans) { for (var i = spans.Count - 1; i >= 0; i--) { @@ -79,8 +79,8 @@ private static void RemoveAdditiveSpans(ArrayBuilder spans) } private static ImmutableArray MergeClassifiedSpans( - ArrayBuilder syntaxSpans, - ArrayBuilder semanticSpans, + SegmentedList syntaxSpans, + SegmentedList semanticSpans, TextSpan widenedSpan) { // The spans produced by the language services may not be ordered @@ -104,21 +104,21 @@ private static void RemoveAdditiveSpans(ArrayBuilder spans) AdjustSpans(syntaxSpans, widenedSpan); AdjustSpans(semanticSpans, widenedSpan); - using var _1 = ArrayBuilder.GetInstance(out var mergedSpans); + using var _1 = Classifier.GetPooledList(out var mergedSpans); MergeParts(syntaxSpans, semanticSpans, mergedSpans); Order(mergedSpans); // The classification service will only produce classifications for things it knows about. i.e. there will // be gaps in what it produces. Fill in those gaps so we have *all* parts of the span classified properly. - using var _2 = ArrayBuilder.GetInstance(out var filledInSpans); + using var _2 = Classifier.GetPooledList(out var filledInSpans); FillInClassifiedSpanGaps(widenedSpan.Start, mergedSpans, filledInSpans); - return filledInSpans.ToImmutable(); + return filledInSpans.ToImmutableArray(); } private static readonly Comparison s_spanComparison = static (s1, s2) => s1.TextSpan.Start - s2.TextSpan.Start; - private static void Order(ArrayBuilder syntaxSpans) + private static void Order(SegmentedList syntaxSpans) => syntaxSpans.Sort(s_spanComparison); /// @@ -126,7 +126,7 @@ private static void Order(ArrayBuilder syntaxSpans) /// name="widenedSpan"/>. Any spans that are entirely outside of are replaced /// with . /// - private static void AdjustSpans(ArrayBuilder spans, TextSpan widenedSpan) + private static void AdjustSpans(SegmentedList spans, TextSpan widenedSpan) { for (var i = 0; i < spans.Count; i++) { @@ -158,7 +158,7 @@ private static void AdjustSpans(ArrayBuilder spans, TextSpan wid } public static void FillInClassifiedSpanGaps( - int startPosition, ArrayBuilder classifiedSpans, ArrayBuilder result) + int startPosition, SegmentedList classifiedSpans, SegmentedList result) { foreach (var span in classifiedSpans) { @@ -188,9 +188,9 @@ private static void AdjustSpans(ArrayBuilder spans, TextSpan wid /// overlap with any semantic parts as well. All final parts will be non-empty. /// private static void MergeParts( - ArrayBuilder syntaxParts, - ArrayBuilder semanticParts, - ArrayBuilder finalParts) + SegmentedList syntaxParts, + SegmentedList semanticParts, + SegmentedList finalParts) { // Create an interval tree so we can easily determine which semantic parts intersect with the // syntactic parts we're looking at. diff --git a/src/Workspaces/Core/Portable/Classification/IClassificationService.cs b/src/Workspaces/Core/Portable/Classification/IClassificationService.cs index 308c6e83f2d31..ef12ddd4b6448 100644 --- a/src/Workspaces/Core/Portable/Classification/IClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/IClassificationService.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -24,13 +25,13 @@ internal interface IClassificationService : ILanguageService /// (i.e. identifiers being classified as keywords). These incorrect results will be patched /// up when the lexical results are superseded by the calls to AddSyntacticClassifications. /// - void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken); + void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); /// /// This method is optional and only should be implemented by languages that support /// syntax. If the language does not support syntax, callers should use /// instead. - void AddSyntacticClassifications(SolutionServices services, SyntaxNode root, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken); + void AddSyntacticClassifications(SolutionServices services, SyntaxNode root, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); /// /// Produce the classifications for the span of text specified. The syntax of the document @@ -38,7 +39,7 @@ internal interface IClassificationService : ILanguageService /// be used to determine if a piece of text that looks like a keyword should actually be /// considered an identifier in its current context. /// - Task AddSyntacticClassificationsAsync(Document document, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken); + Task AddSyntacticClassificationsAsync(Document document, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); /// /// Produce the classifications for the span of text specified. Semantics of the language can be used to @@ -50,13 +51,13 @@ internal interface IClassificationService : ILanguageService /// This will not include classifications for embedded language constructs in string literals. For that use /// . /// - Task AddSemanticClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken); + Task AddSemanticClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken); /// /// Produce the classifications for embedded language string literals (e.g. Regex/Json strings) in the span of /// text specified. /// - Task AddEmbeddedLanguageClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken); + Task AddEmbeddedLanguageClassificationsAsync(Document document, TextSpan textSpan, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken); /// /// Adjust a classification from a previous version of text accordingly based on the current diff --git a/src/Workspaces/Core/Portable/Classification/IEmbeddedLanguageClassificationService.cs b/src/Workspaces/Core/Portable/Classification/IEmbeddedLanguageClassificationService.cs index 8283310cad01a..f04b99ca64fed 100644 --- a/src/Workspaces/Core/Portable/Classification/IEmbeddedLanguageClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/IEmbeddedLanguageClassificationService.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -16,7 +17,7 @@ internal interface IEmbeddedLanguageClassificationService : ILanguageService Document document, TextSpan textSpan, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken); void AddEmbeddedLanguageClassifications( @@ -24,7 +25,7 @@ internal interface IEmbeddedLanguageClassificationService : ILanguageService SemanticModel semanticModel, TextSpan textSpan, ClassificationOptions options, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs index b48c975274ae1..dfe68ee04cb18 100644 --- a/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/IRemoteSemanticClassificationService.cs @@ -7,12 +7,11 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification { @@ -50,10 +49,16 @@ internal interface IRemoteSemanticClassificationService internal sealed class SerializableClassifiedSpans { [DataMember(Order = 0)] - public List? ClassificationTypes; + public readonly ImmutableArray ClassificationTypes; [DataMember(Order = 1)] - public List? ClassificationTriples; + public readonly ImmutableArray ClassificationTriples; + + public SerializableClassifiedSpans(ImmutableArray classificationTypes, ImmutableArray classificationTriples) + { + ClassificationTypes = classificationTypes; + ClassificationTriples = classificationTriples; + } internal static SerializableClassifiedSpans Dehydrate(ImmutableArray classifiedSpans) { @@ -63,8 +68,8 @@ internal static SerializableClassifiedSpans Dehydrate(ImmutableArray classifiedSpans, Dictionary classificationTypeToId) { - var classificationTypes = new List(); - var classificationTriples = new List(capacity: classifiedSpans.Length * 3); + using var _1 = ArrayBuilder.GetInstance(out var classificationTypes); + using var _2 = ArrayBuilder.GetInstance(capacity: classifiedSpans.Length * 3, out var classificationTriples); foreach (var classifiedSpan in classifiedSpans) { @@ -82,19 +87,14 @@ private static SerializableClassifiedSpans Dehydrate(ImmutableArray classifiedSpans) + internal void Rehydrate(SegmentedList classifiedSpans) { - Contract.ThrowIfNull(ClassificationTypes); - Contract.ThrowIfNull(ClassificationTriples); - - for (var i = 0; i < ClassificationTriples.Count; i += 3) + for (int i = 0, n = ClassificationTriples.Length; i < n; i += 3) { classifiedSpans.Add(new ClassifiedSpan( ClassificationTypes[ClassificationTriples[i + 0]], diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs index e9f7878d3e34e..a2624fc716d67 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractNameSyntaxClassifier.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 Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -30,14 +31,10 @@ internal abstract class AbstractNameSyntaxClassifier : AbstractSyntaxClassifier protected static void TryClassifyStaticSymbol( ISymbol symbol, TextSpan span, - ArrayBuilder result) + SegmentedList result) { - if (!IsStaticSymbol(symbol)) - { - return; - } - - result.Add(new ClassifiedSpan(span, ClassificationTypeNames.StaticSymbol)); + if (IsStaticSymbol(symbol)) + result.Add(new ClassifiedSpan(span, ClassificationTypeNames.StaticSymbol)); } protected static bool IsStaticSymbol(ISymbol symbol) diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.Worker.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.Worker.cs index 6d16dd61c9ee7..bbf43cab7ab29 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.Worker.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.Worker.cs @@ -3,10 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -19,18 +21,20 @@ internal partial class AbstractSyntaxClassificationService private readonly SemanticModel _semanticModel; private readonly SyntaxTree _syntaxTree; private readonly TextSpan _textSpan; - private readonly ArrayBuilder _list; + private readonly SegmentedList _list; private readonly CancellationToken _cancellationToken; private readonly Func> _getNodeClassifiers; private readonly Func> _getTokenClassifiers; - private readonly HashSet _set; + private readonly SegmentedHashSet _set; private readonly Stack _pendingNodes; private readonly ClassificationOptions _options; + private static readonly ObjectPool> s_listPool = new(() => new()); + private Worker( SemanticModel semanticModel, TextSpan textSpan, - ArrayBuilder list, + SegmentedList list, Func> getNodeClassifiers, Func> getTokenClassifiers, ClassificationOptions options, @@ -46,32 +50,33 @@ internal partial class AbstractSyntaxClassificationService _options = options; // get one from pool - _set = SharedPools.Default>().AllocateAndClear(); + _set = SharedPools.Default>().AllocateAndClear(); _pendingNodes = SharedPools.Default>().AllocateAndClear(); } internal static void Classify( SemanticModel semanticModel, TextSpan textSpan, - ArrayBuilder list, + SegmentedList list, Func> getNodeClassifiers, Func> getTokenClassifiers, ClassificationOptions options, CancellationToken cancellationToken) { - var worker = new Worker(semanticModel, textSpan, list, getNodeClassifiers, getTokenClassifiers, options, cancellationToken); + using var worker = new Worker(semanticModel, textSpan, list, getNodeClassifiers, getTokenClassifiers, options, cancellationToken); - try - { - worker._pendingNodes.Push(worker._syntaxTree.GetRoot(cancellationToken)); - worker.ProcessNodes(); - } - finally - { - // release collections to the pool - SharedPools.Default>().ClearAndFree(worker._set); - SharedPools.Default>().ClearAndFree(worker._pendingNodes); - } + worker._pendingNodes.Push(worker._syntaxTree.GetRoot(cancellationToken)); + worker.ProcessNodes(); + } + + public void Dispose() + { + // Deliberately do not call ClearAndFree for the set as we can easily have a set that goes past the + // threshold simply with a single classified screen. This allows reuse of those sets without causing + // lots of garbage. + _set.Clear(); + SharedPools.Default>().Free(_set); + SharedPools.Default>().ClearAndFree(this._pendingNodes); } private void AddClassification(TextSpan textSpan, string type) @@ -121,27 +126,23 @@ private void ClassifyNodeOrToken(SyntaxNodeOrToken nodeOrToken) private void ClassifyNode(SyntaxNode syntax) { - using var _ = ArrayBuilder.GetInstance(out var result); + using var obj = s_listPool.GetPooledObject(); + var list = obj.Object; foreach (var classifier in _getNodeClassifiers(syntax)) { _cancellationToken.ThrowIfCancellationRequested(); - result.Clear(); - classifier.AddClassifications(syntax, _semanticModel, _options, result, _cancellationToken); - AddClassifications(result); + list.Clear(); + classifier.AddClassifications(syntax, _semanticModel, _options, list, _cancellationToken); + AddClassifications(list); } } - private void AddClassifications(ArrayBuilder classifications) + private void AddClassifications(SegmentedList classifications) { - if (classifications != null) - { - foreach (var classification in classifications) - { - AddClassification(classification); - } - } + foreach (var classification in classifications) + AddClassification(classification); } private void AddClassification(ClassifiedSpan classification) @@ -156,15 +157,16 @@ private void ClassifyToken(SyntaxToken syntax) { ClassifyStructuredTrivia(syntax.LeadingTrivia); - using var _ = ArrayBuilder.GetInstance(out var result); + using var obj = s_listPool.GetPooledObject(); + var list = obj.Object; foreach (var classifier in _getTokenClassifiers(syntax)) { _cancellationToken.ThrowIfCancellationRequested(); - result.Clear(); - classifier.AddClassifications(syntax, _semanticModel, _options, result, _cancellationToken); - AddClassifications(result); + list.Clear(); + classifier.AddClassifications(syntax, _semanticModel, _options, list, _cancellationToken); + AddClassifications(list); } ClassifyStructuredTrivia(syntax.TrailingTrivia); diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs index ef3f1dd15f895..90510688f70cf 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -21,8 +22,8 @@ protected AbstractSyntaxClassificationService() { } - public abstract void AddLexicalClassifications(SourceText text, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken); - public abstract void AddSyntacticClassifications(SyntaxNode root, TextSpan textSpan, ArrayBuilder result, CancellationToken cancellationToken); + public abstract void AddLexicalClassifications(SourceText text, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); + public abstract void AddSyntacticClassifications(SyntaxNode root, TextSpan textSpan, SegmentedList result, CancellationToken cancellationToken); public abstract ImmutableArray GetDefaultSyntaxClassifiers(); public abstract ClassifiedSpan FixClassification(SourceText text, ClassifiedSpan classifiedSpan); @@ -34,7 +35,7 @@ protected AbstractSyntaxClassificationService() ClassificationOptions options, Func> getNodeClassifiers, Func> getTokenClassifiers, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken) { try @@ -53,7 +54,7 @@ protected AbstractSyntaxClassificationService() TextSpan textSpan, Func> getNodeClassifiers, Func> getTokenClassifiers, - ArrayBuilder result, + SegmentedList result, ClassificationOptions options, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassifier.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassifier.cs index d8da7aacbb464..fd3d10ec5d7f3 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassifier.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassifier.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Classification.Classifiers { @@ -18,11 +18,11 @@ protected AbstractSyntaxClassifier() protected static string? GetClassificationForType(ITypeSymbol type) => type.GetClassification(); - public virtual void AddClassifications(SyntaxNode syntax, SemanticModel semanticModel, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + public virtual void AddClassifications(SyntaxNode syntax, SemanticModel semanticModel, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { } - public virtual void AddClassifications(SyntaxToken syntax, SemanticModel semanticModel, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken) + public virtual void AddClassifications(SyntaxToken syntax, SemanticModel semanticModel, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken) { } diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassificationService.cs index 8581ece092635..532008ed47698 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassificationService.cs @@ -7,8 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification.Classifiers; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Classification @@ -20,14 +20,14 @@ internal interface ISyntaxClassificationService : ILanguageService /// void AddLexicalClassifications(SourceText text, TextSpan textSpan, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken); /// void AddSyntacticClassifications( SyntaxNode root, TextSpan textSpan, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken); /// @@ -37,7 +37,7 @@ internal interface ISyntaxClassificationService : ILanguageService ClassificationOptions options, Func> getNodeClassifiers, Func> getTokenClassifiers, - ArrayBuilder result, + SegmentedList result, CancellationToken cancellationToken); /// @@ -46,7 +46,7 @@ internal interface ISyntaxClassificationService : ILanguageService TextSpan textSpan, Func> getNodeClassifiers, Func> getTokenClassifiers, - ArrayBuilder result, + SegmentedList result, ClassificationOptions options, CancellationToken cancellationToken); diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassifier.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassifier.cs index 5c0b72ac6acb7..3c6c50526f7dc 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassifier.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/ISyntaxClassifier.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Classification.Classifiers { @@ -24,11 +24,11 @@ internal interface ISyntaxClassifier /// /// This method will be called for all nodes that match the types specified by the property. /// - void AddClassifications(SyntaxNode node, SemanticModel semanticModel, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken); + void AddClassifications(SyntaxNode node, SemanticModel semanticModel, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken); /// /// This method will be called for all tokens that match the kinds specified by the property. /// - void AddClassifications(SyntaxToken token, SemanticModel semanticModel, ClassificationOptions options, ArrayBuilder result, CancellationToken cancellationToken); + void AddClassifications(SyntaxToken token, SemanticModel semanticModel, ClassificationOptions options, SegmentedList result, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CodeFixes/CodeFixContext.cs b/src/Workspaces/Core/Portable/CodeFixes/CodeFixContext.cs index 8b82757c81374..696c036d4a369 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/CodeFixContext.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/CodeFixContext.cs @@ -17,9 +17,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes /// /// Context for code fixes provided by a . /// -#pragma warning disable CS0612 // Type or member is obsolete - public readonly struct CodeFixContext : ITypeScriptCodeFixContext -#pragma warning restore + public readonly struct CodeFixContext { private readonly TextDocument _document; private readonly TextSpan _span; @@ -82,15 +80,6 @@ public Document Document /// internal readonly CodeActionOptionsProvider Options; - /// - /// TypeScript specific. https://github.com/dotnet/roslyn/issues/61122 - /// - private readonly bool _isBlocking; - - [Obsolete] - bool ITypeScriptCodeFixContext.IsBlocking - => _isBlocking; - /// /// Creates a code fix context to be passed into method. /// @@ -120,7 +109,6 @@ bool ITypeScriptCodeFixContext.IsBlocking diagnostics, registerCodeFix, CodeActionOptions.DefaultProvider, - isBlocking: false, cancellationToken) { } @@ -153,7 +141,6 @@ bool ITypeScriptCodeFixContext.IsBlocking diagnostics, registerCodeFix, CodeActionOptions.DefaultProvider, - isBlocking: false, cancellationToken) { } @@ -180,7 +167,6 @@ bool ITypeScriptCodeFixContext.IsBlocking ImmutableArray.Create(diagnostic), registerCodeFix, CodeActionOptions.DefaultProvider, - isBlocking: false, cancellationToken) { } @@ -206,7 +192,6 @@ bool ITypeScriptCodeFixContext.IsBlocking ImmutableArray.Create(diagnostic), registerCodeFix, CodeActionOptions.DefaultProvider, - isBlocking: false, cancellationToken) { } @@ -217,7 +202,6 @@ bool ITypeScriptCodeFixContext.IsBlocking ImmutableArray diagnostics, Action> registerCodeFix, CodeActionOptionsProvider options, - bool isBlocking, CancellationToken cancellationToken) { VerifyDiagnosticsArgument(diagnostics, span); @@ -227,7 +211,6 @@ bool ITypeScriptCodeFixContext.IsBlocking _diagnostics = diagnostics; _registerCodeFix = registerCodeFix ?? throw new ArgumentNullException(nameof(registerCodeFix)); Options = options; - _isBlocking = isBlocking; _cancellationToken = cancellationToken; } @@ -306,10 +289,4 @@ private static void VerifyDiagnosticsArgument(ImmutableArray diagnos } } } - - [Obsolete] - internal interface ITypeScriptCodeFixContext - { - bool IsBlocking { get; } - } } diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs index abe8dc71a6a09..96aa763e65adf 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs @@ -155,7 +155,7 @@ public override IEnumerable GetSupportedFixAllScopes() // Create a context that will add the reported code actions into this using var _2 = ArrayBuilder.GetInstance(out var codeActions); var action = GetRegisterCodeFixAction(fixAllContext.CodeActionEquivalenceKey, codeActions); - var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), action, fixAllContext.State.CodeActionOptionsProvider, isBlocking: false, cancellationToken); + var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), action, fixAllContext.State.CodeActionOptionsProvider, cancellationToken); // Wait for the all the code actions to be reported for this diagnostic. var registerTask = fixAllContext.CodeFixProvider.RegisterCodeFixesAsync(context) ?? Task.CompletedTask; diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs index 8662048d26ed1..f4f63e29b894c 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContextHelper.cs @@ -130,9 +130,11 @@ async Task AddDocumentDiagnosticsAsync(ConcurrentDictionary>(); - foreach (var (document, diagnosticsForDocument) in diagnostics.GroupBy(d => solution.GetDocument(d.Location.SourceTree))) + + // NOTE: We use 'GetTextDocumentForLocation' extension to ensure we also handle external location diagnostics in non-C#/VB languages. + foreach (var (textDocument, diagnosticsForDocument) in diagnostics.GroupBy(d => solution.GetTextDocumentForLocation(d.Location))) { - if (document is null) + if (textDocument is not Document document) continue; cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringContext.cs b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringContext.cs index 49def78841ad3..ae83bb0b70e08 100644 --- a/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringContext.cs +++ b/src/Workspaces/Core/Portable/CodeRefactorings/CodeRefactoringContext.cs @@ -14,9 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings /// /// Context for code refactorings provided by a . /// -#pragma warning disable CS0612 // Type or member is obsolete - public readonly struct CodeRefactoringContext : ITypeScriptCodeRefactoringContext -#pragma warning restore + public readonly struct CodeRefactoringContext { /// /// Document corresponding to the to refactor. @@ -58,15 +56,6 @@ public Document Document internal readonly CodeActionOptionsProvider Options; - /// - /// TypeScript specific. - /// - private readonly bool _isBlocking; - - [Obsolete] - bool ITypeScriptCodeRefactoringContext.IsBlocking - => _isBlocking; - private readonly Action _registerRefactoring; /// @@ -78,7 +67,7 @@ bool ITypeScriptCodeRefactoringContext.IsBlocking TextSpan span, Action registerRefactoring, CancellationToken cancellationToken) - : this(document, span, (action, textSpan) => registerRefactoring(action), CodeActionOptions.DefaultProvider, isBlocking: false, cancellationToken) + : this(document, span, (action, textSpan) => registerRefactoring(action), CodeActionOptions.DefaultProvider, cancellationToken) { } /// @@ -89,7 +78,7 @@ bool ITypeScriptCodeRefactoringContext.IsBlocking TextSpan span, Action registerRefactoring, CancellationToken cancellationToken) - : this(document, span, (action, textSpan) => registerRefactoring(action), CodeActionOptions.DefaultProvider, isBlocking: false, cancellationToken) + : this(document, span, (action, textSpan) => registerRefactoring(action), CodeActionOptions.DefaultProvider, cancellationToken) { } /// @@ -100,7 +89,6 @@ bool ITypeScriptCodeRefactoringContext.IsBlocking TextSpan span, Action registerRefactoring, CodeActionOptionsProvider options, - bool isBlocking, CancellationToken cancellationToken) { // NOTE/TODO: Don't make this overload public & obsolete the `Action registerRefactoring` @@ -109,7 +97,6 @@ bool ITypeScriptCodeRefactoringContext.IsBlocking Span = span; _registerRefactoring = registerRefactoring ?? throw new ArgumentNullException(nameof(registerRefactoring)); Options = options; - _isBlocking = isBlocking; CancellationToken = cancellationToken; } @@ -147,10 +134,4 @@ internal void Deconstruct(out Document document, out TextSpan span, out Cancella cancellationToken = CancellationToken; } } - - [Obsolete] - internal interface ITypeScriptCodeRefactoringContext - { - bool IsBlocking { get; } - } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs index 2b1803f50dee5..6097dc379543f 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -74,11 +75,20 @@ public static string GetAnalyzerId(this DiagnosticAnalyzer analyzer) return GetAssemblyQualifiedName(type); } + /// + /// Cache of a to its . We cache this as the latter + /// computes and allocates expensively every time it is called. + /// + private static ImmutableSegmentedDictionary s_typeToAssemblyQualifiedName = ImmutableSegmentedDictionary.Empty; + private static string GetAssemblyQualifiedName(Type type) { // AnalyzerFileReference now includes things like versions, public key as part of its identity. // so we need to consider them. - return type.AssemblyQualifiedName ?? throw ExceptionUtilities.UnexpectedValue(type); + return RoslynImmutableInterlocked.GetOrAdd( + ref s_typeToAssemblyQualifiedName, + type, + static type => type.AssemblyQualifiedName ?? throw ExceptionUtilities.UnexpectedValue(type)); } public static async Task> ToResultBuilderMapAsync( diff --git a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCodeFixContextExtensions.cs b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCodeFixContextExtensions.cs deleted file mode 100644 index d1e9c7113d511..0000000000000 --- a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptCodeFixContextExtensions.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.CodeFixes; -using Microsoft.CodeAnalysis.CodeRefactorings; - -namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api -{ - internal static class VSTypeScriptCodeFixContextExtensions - { - public static bool IsBlocking(this CodeFixContext context) -#pragma warning disable CS0612 // Type or member is obsolete - => ((ITypeScriptCodeFixContext)context).IsBlocking; -#pragma warning restore - - public static bool IsBlocking(this CodeRefactoringContext context) -#pragma warning disable CS0612 // Type or member is obsolete - => ((ITypeScriptCodeRefactoringContext)context).IsBlocking; -#pragma warning restore - } -} diff --git a/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs b/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs index b248524189590..348abb8be4f79 100644 --- a/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs +++ b/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.ExtractMethod; [DataContract] internal readonly record struct ExtractMethodOptions { - [DataMember] public bool DontPutOutOrRefOnStruct { get; init; } = true; + [DataMember] public bool DoNotPutOutOrRefOnStruct { get; init; } = true; public ExtractMethodOptions() { diff --git a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs index fc5d8e3af64bb..33d31296177f7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Declarations/DeclarationFinder_AllDeclarations.cs @@ -63,7 +63,7 @@ internal static partial class DeclarationFinder // Lazily produce the compilation. We don't want to incur any costs (especially source generators) if there // are no results for this query in this project. - var lazyCompilation = new AsyncLazy(project.GetRequiredCompilationAsync, cacheResult: true); + var lazyCompilation = AsyncLazy.Create(project.GetRequiredCompilationAsync); if (project.SupportsCompilation) { @@ -114,12 +114,12 @@ async Task SearchMetadataReferencesAsync() { using var _ = ArrayBuilder.GetInstance(out var buffer); - var lazyAssembly = new AsyncLazy(async cancellationToken => + var lazyAssembly = AsyncLazy.Create(async cancellationToken => { var compilation = await lazyCompilation.GetValueAsync(cancellationToken).ConfigureAwait(false); var assemblySymbol = compilation.GetAssemblyOrModuleSymbol(peReference) as IAssemblySymbol; return assemblySymbol; - }, cacheResult: true); + }); await AddMetadataDeclarationsWithNormalQueryAsync( project, lazyAssembly, peReference, diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index b2dc1097a2df3..90bbb41b2005c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; @@ -23,6 +24,12 @@ namespace Microsoft.CodeAnalysis.FindSymbols /// internal static partial class DependentProjectsFinder { + /// + /// Cache from the for a particular to the + /// name of the defined by it. + /// + private static ImmutableDictionary s_metadataIdToAssemblyName = ImmutableDictionary.Empty; + public static async Task> GetDependentProjectsAsync( Solution solution, ImmutableArray symbols, IImmutableSet projects, CancellationToken cancellationToken) { @@ -276,28 +283,70 @@ private static bool HasReferenceToAssembly(Project project, string assemblyName, { Contract.ThrowIfFalse(project.SupportsCompilation); - if (!project.TryGetCompilation(out var compilation)) - { - // WORKAROUND: - // perf check metadata reference using newly created empty compilation with only metadata references. - compilation = project.Services.GetRequiredService().CreateCompilation( - project.AssemblyName, project.CompilationOptions!); + // Do two passes. One that attempts to find a result without ever realizing a Compilation, and one that + // tries again, but which is willing to create the Compilation if necessary. - compilation = compilation.AddReferences(project.MetadataReferences); - } + using var _ = ArrayBuilder<(PortableExecutableReference reference, MetadataId metadataId)>.GetInstance(out var uncomputedReferences); foreach (var reference in project.MetadataReferences) { cancellationToken.ThrowIfCancellationRequested(); - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol symbol && - symbol.Name == assemblyName) + if (reference is not PortableExecutableReference peReference) + continue; + + var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(peReference); + if (metadataId is null) + continue; + + if (!s_metadataIdToAssemblyName.TryGetValue(metadataId, out var name)) { + uncomputedReferences.Add((peReference, metadataId)); + continue; + } + + if (name == assemblyName) return true; + } + + if (uncomputedReferences.Count == 0) + return false; + + Compilation? compilation = null; + + foreach (var (peReference, metadataId) in uncomputedReferences) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!s_metadataIdToAssemblyName.TryGetValue(metadataId, out var name)) + { + // Defer creating the compilation till needed. + CreateCompilation(project, ref compilation); + if (compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol { Name: string metadataAssemblyName }) + name = ImmutableInterlocked.GetOrAdd(ref s_metadataIdToAssemblyName, metadataId, metadataAssemblyName); } + + if (name == assemblyName) + return true; } return false; + + static void CreateCompilation(Project project, [NotNull] ref Compilation? compilation) + { + if (compilation != null) + return; + + // Use the project's compilation if it has one. + if (project.TryGetCompilation(out compilation)) + return; + + // Perf: check metadata reference using newly created empty compilation with only metadata references. + var factory = project.Services.GetRequiredService(); + compilation = factory + .CreateCompilation(project.AssemblyName, project.CompilationOptions!) + .AddReferences(project.MetadataReferences); + } } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs index f299b9b02e46c..b56aa39b6b6b8 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder.cs @@ -240,15 +240,16 @@ async Task AddDescendantSourceTypesInProjectAsync(SymbolSet result, Project proj } async Task AddMatchingTypesAsync( - MultiDictionary documentToInfos, + MultiDictionary documentToInfos, SymbolSet result, Func? predicateOpt) { - foreach (var (document, infos) in documentToInfos) + foreach (var (documentId, infos) in documentToInfos) { cancellationToken.ThrowIfCancellationRequested(); Debug.Assert(infos.Count > 0); + var document = await solution.GetRequiredDocumentAsync(documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); cachedModels.Add(semanticModel); @@ -271,10 +272,11 @@ async Task AddDescendantSourceTypesInProjectAsync(SymbolSet result, Project proj async Task AddSourceTypesThatDeriveFromNameAsync(SymbolSet result, string name) { - foreach (var (document, info) in projectIndex.NamedTypes[name]) + foreach (var (documentId, info) in projectIndex.NamedTypes[name]) { cancellationToken.ThrowIfCancellationRequested(); + var document = await solution.GetRequiredDocumentAsync(documentId, includeSourceGenerated: true, cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); cachedModels.Add(semanticModel); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs index d1902ca0bfcef..abb7813a4db36 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs @@ -7,24 +7,29 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { internal static partial class DependentTypeFinder { - private class ProjectIndex + private sealed class ProjectIndex { - private static readonly ConditionalWeakTable> s_projectToIndex = - new(); + private static readonly ConditionalWeakTable> s_projectToIndex = new(); - public readonly MultiDictionary ClassesAndRecordsThatMayDeriveFromSystemObject; - public readonly MultiDictionary ValueTypes; - public readonly MultiDictionary Enums; - public readonly MultiDictionary Delegates; - public readonly MultiDictionary NamedTypes; + public readonly MultiDictionary ClassesAndRecordsThatMayDeriveFromSystemObject; + public readonly MultiDictionary ValueTypes; + public readonly MultiDictionary Enums; + public readonly MultiDictionary Delegates; + public readonly MultiDictionary NamedTypes; - public ProjectIndex(MultiDictionary classesAndRecordsThatMayDeriveFromSystemObject, MultiDictionary valueTypes, MultiDictionary enums, MultiDictionary delegates, MultiDictionary namedTypes) + public ProjectIndex( + MultiDictionary classesAndRecordsThatMayDeriveFromSystemObject, + MultiDictionary valueTypes, + MultiDictionary enums, + MultiDictionary delegates, + MultiDictionary namedTypes) { ClassesAndRecordsThatMayDeriveFromSystemObject = classesAndRecordsThatMayDeriveFromSystemObject; ValueTypes = valueTypes; @@ -36,11 +41,11 @@ public ProjectIndex(MultiDictionary classesAndReco public static Task GetIndexAsync( Project project, CancellationToken cancellationToken) { - if (!s_projectToIndex.TryGetValue(project, out var lazyIndex)) + if (!s_projectToIndex.TryGetValue(project.State, out var lazyIndex)) { lazyIndex = s_projectToIndex.GetValue( - project, p => new AsyncLazy( - c => ProjectIndex.CreateIndexAsync(p, c), cacheResult: true)); + project.State, p => new AsyncLazy( + c => CreateIndexAsync(project, c))); } return lazyIndex.GetValueAsync(cancellationToken); @@ -48,41 +53,42 @@ public ProjectIndex(MultiDictionary classesAndReco private static async Task CreateIndexAsync(Project project, CancellationToken cancellationToken) { - var classesThatMayDeriveFromSystemObject = new MultiDictionary(); - var valueTypes = new MultiDictionary(); - var enums = new MultiDictionary(); - var delegates = new MultiDictionary(); + var classesThatMayDeriveFromSystemObject = new MultiDictionary(); + var valueTypes = new MultiDictionary(); + var enums = new MultiDictionary(); + var delegates = new MultiDictionary(); - var namedTypes = new MultiDictionary( + var namedTypes = new MultiDictionary( project.Services.GetRequiredService().StringComparer); - foreach (var document in project.Documents) + // Avoid realizing actual Document instances here. We don't need them, and it can allocate a lot of + // memory as we do background indexing. + foreach (var (documentId, document) in project.State.DocumentStates.States) { - var syntaxTreeIndex = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); + var syntaxTreeIndex = await TopLevelSyntaxTreeIndex.GetRequiredIndexAsync( + SolutionKey.ToSolutionKey(project.Solution), project.State, document, cancellationToken).ConfigureAwait(false); foreach (var info in syntaxTreeIndex.DeclaredSymbolInfos) { switch (info.Kind) { case DeclaredSymbolInfoKind.Class: case DeclaredSymbolInfoKind.Record: - classesThatMayDeriveFromSystemObject.Add(document, info); + classesThatMayDeriveFromSystemObject.Add(documentId, info); break; case DeclaredSymbolInfoKind.Enum: - enums.Add(document, info); + enums.Add(documentId, info); break; case DeclaredSymbolInfoKind.Struct: case DeclaredSymbolInfoKind.RecordStruct: - valueTypes.Add(document, info); + valueTypes.Add(documentId, info); break; case DeclaredSymbolInfoKind.Delegate: - delegates.Add(document, info); + delegates.Add(documentId, info); break; } foreach (var inheritanceName in info.InheritanceNames) - { - namedTypes.Add(inheritanceName, (document, info)); - } + namedTypes.Add(inheritanceName, (documentId, info)); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex.cs index 92c8d0cb4fe11..988bbe4312d1c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols @@ -16,9 +15,9 @@ internal abstract partial class AbstractSyntaxIndex where TIndex : AbstractSyntaxIndex { protected delegate TIndex? IndexReader(StringTable stringTable, ObjectReader reader, Checksum? checksum); - protected delegate TIndex IndexCreator(Document document, SyntaxNode root, Checksum checksum, CancellationToken cancellationToken); + protected delegate TIndex IndexCreator(ProjectState project, SyntaxNode root, Checksum checksum, CancellationToken cancellationToken); - private static readonly ConditionalWeakTable s_documentToIndex = new(); + private static readonly ConditionalWeakTable s_documentToIndex = new(); private static readonly ConditionalWeakTable s_documentIdToIndex = new(); protected AbstractSyntaxIndex(Checksum? checksum) @@ -26,19 +25,22 @@ protected AbstractSyntaxIndex(Checksum? checksum) this.Checksum = checksum; } - protected static async ValueTask GetRequiredIndexAsync(Document document, IndexReader read, IndexCreator create, CancellationToken cancellationToken) + protected static async ValueTask GetRequiredIndexAsync( + SolutionKey solutionKey, ProjectState project, DocumentState document, IndexReader read, IndexCreator create, CancellationToken cancellationToken) { - var index = await GetIndexAsync(document, read, create, cancellationToken).ConfigureAwait(false); + var index = await GetIndexAsync(solutionKey, project, document, read, create, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(index); return index; } - protected static ValueTask GetIndexAsync(Document document, IndexReader read, IndexCreator create, CancellationToken cancellationToken) - => GetIndexAsync(document, loadOnly: false, read, create, cancellationToken); + protected static ValueTask GetIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, IndexReader read, IndexCreator create, CancellationToken cancellationToken) + => GetIndexAsync(solutionKey, project, document, loadOnly: false, read, create, cancellationToken); [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] protected static async ValueTask GetIndexAsync( - Document document, + SolutionKey solutionKey, + ProjectState project, + DocumentState document, bool loadOnly, IndexReader read, IndexCreator create, @@ -51,13 +53,11 @@ protected static async ValueTask GetRequiredIndexAsync(Document document // return it with no additional work. if (!s_documentToIndex.TryGetValue(document, out var index)) { - index = await GetIndexWorkerAsync(document, loadOnly, read, create, cancellationToken).ConfigureAwait(false); + index = await GetIndexWorkerAsync(solutionKey, project, document, loadOnly, read, create, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(index != null || loadOnly == true, "Result can only be null if 'loadOnly: true' was passed."); - if (index == null && loadOnly) - { + if (index == null) return null; - } // Populate our caches with this data. s_documentToIndex.GetValue(document, _ => index); @@ -69,18 +69,17 @@ protected static async ValueTask GetRequiredIndexAsync(Document document } private static async Task GetIndexWorkerAsync( - Document document, + SolutionKey solutionKey, + ProjectState project, + DocumentState document, bool loadOnly, IndexReader read, IndexCreator create, CancellationToken cancellationToken) { - if (!document.SupportsSyntaxTree) - return null; - - var (textChecksum, textAndDirectivesChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); + var (textChecksum, textAndDirectivesChecksum) = await GetChecksumsAsync(project, document, cancellationToken).ConfigureAwait(false); - // Check if we have an index for a previous version of this document. If our + // Check if we have an index for a another version of this document. If our // checksums match, we can just use that. if (s_documentIdToIndex.TryGetValue(document.Id, out var index) && (index?.Checksum == textChecksum || index?.Checksum == textAndDirectivesChecksum)) @@ -91,21 +90,22 @@ protected static async ValueTask GetRequiredIndexAsync(Document document } // What we have in memory isn't valid. Try to load from the persistence service. - index = await LoadAsync(document, textChecksum, textAndDirectivesChecksum, read, cancellationToken).ConfigureAwait(false); + index = await LoadAsync(solutionKey, project, document, textChecksum, textAndDirectivesChecksum, read, cancellationToken).ConfigureAwait(false); if (index != null || loadOnly) return index; // alright, we don't have cached information, re-calculate them here. - index = await CreateIndexAsync(document, textChecksum, textAndDirectivesChecksum, create, cancellationToken).ConfigureAwait(false); + index = await CreateIndexAsync(project, document, textChecksum, textAndDirectivesChecksum, create, cancellationToken).ConfigureAwait(false); // okay, persist this info - await index.SaveAsync(document, cancellationToken).ConfigureAwait(false); + await index.SaveAsync(solutionKey, project, document, cancellationToken).ConfigureAwait(false); return index; } private static async Task CreateIndexAsync( - Document document, + ProjectState project, + DocumentState document, Checksum textChecksum, Checksum textAndDirectivesChecksum, IndexCreator create, @@ -113,8 +113,9 @@ protected static async ValueTask GetRequiredIndexAsync(Document document { Contract.ThrowIfFalse(document.SupportsSyntaxTree); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var syntaxKinds = document.GetRequiredLanguageService(); + var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var syntaxKinds = project.LanguageServices.GetRequiredService(); // if the tree contains `#if`-directives, then include the directives-checksum info in the checksum we // produce. We don't want to consider the data reusable if the user changes their parse-option pp-directives @@ -130,7 +131,7 @@ protected static async ValueTask GetRequiredIndexAsync(Document document var checksum = root.ContainsDirectives && ContainsIfDirective(root, ifDirectiveKind) ? textAndDirectivesChecksum : textChecksum; - return create(document, root, checksum, cancellationToken); + return create(project, root, checksum, cancellationToken); } private static bool ContainsIfDirective(SyntaxNode node, int ifDirectiveKind) diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 1f87a831d2ab4..22d86e27cb15f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -29,15 +29,17 @@ internal partial class AbstractSyntaxIndex : IObjectWritable public readonly Checksum? Checksum; protected static async Task LoadAsync( - Document document, + SolutionKey solutionKey, + ProjectState project, + DocumentState document, Checksum textChecksum, Checksum textAndDirectivesChecksum, IndexReader read, CancellationToken cancellationToken) { - var storageService = document.Project.Solution.Services.GetPersistentStorageService(); - var documentKey = DocumentKey.ToDocumentKey(document); - var stringTable = SyntaxTreeIndex.GetStringTable(document.Project); + var storageService = project.LanguageServices.SolutionServices.GetPersistentStorageService(); + var documentKey = DocumentKey.ToDocumentKey(ProjectKey.ToProjectKey(solutionKey, project), document); + var stringTable = SyntaxTreeIndex.GetStringTable(project); // Try to read from the DB using either checksum. If the writer determined there were no pp-directives, // then we may match it using textChecksum. If there were pp directives, then we may match is using @@ -83,7 +85,9 @@ internal partial class AbstractSyntaxIndex : IObjectWritable } public static async ValueTask<(Checksum textOnlyChecksum, Checksum textAndDirectivesChecksum)> GetChecksumsAsync( - Document document, CancellationToken cancellationToken) + ProjectState project, + DocumentState document, + CancellationToken cancellationToken) { // Since we build the SyntaxTreeIndex from a SyntaxTree, we need our checksum to change any time the // SyntaxTree could have changed. Right now, that can only happen if the text of the document changes, or @@ -108,9 +112,7 @@ internal partial class AbstractSyntaxIndex : IObjectWritable // // We also want the checksum to change any time our serialization format changes. If the format has // changed, all previous versions should be invalidated. - var project = document.Project; - - var documentChecksumState = await document.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var documentChecksumState = await document.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var directivesChecksum = s_ppDirectivesToChecksum.GetValue( project.ParseOptions!, @@ -123,21 +125,36 @@ internal partial class AbstractSyntaxIndex : IObjectWritable } private Task SaveAsync( - Document document, CancellationToken cancellationToken) + SolutionKey solutionKey, + ProjectState project, + DocumentState document, + CancellationToken cancellationToken) { - var solution = document.Project.Solution; - var persistentStorageService = solution.Services.GetPersistentStorageService(); - return SaveAsync(persistentStorageService, document, cancellationToken); + var persistentStorageService = project.LanguageServices.SolutionServices.GetPersistentStorageService(); + return SaveAsync(solutionKey, project, document, persistentStorageService, cancellationToken); } - public async Task SaveAsync( - IChecksummedPersistentStorageService persistentStorageService, Document document, CancellationToken cancellationToken) + public Task SaveAsync( + Document document, IChecksummedPersistentStorageService persistentStorageService) { - var solution = document.Project.Solution; + return SaveAsync( + SolutionKey.ToSolutionKey(document.Project.Solution), + document.Project.State, + (DocumentState)document.State, + persistentStorageService, + CancellationToken.None); + } + private async Task SaveAsync( + SolutionKey solutionKey, + ProjectState project, + DocumentState document, + IChecksummedPersistentStorageService persistentStorageService, + CancellationToken cancellationToken) + { try { - var storage = await persistentStorageService.GetStorageAsync(SolutionKey.ToSolutionKey(solution), cancellationToken).ConfigureAwait(false); + var storage = await persistentStorageService.GetStorageAsync(solutionKey, cancellationToken).ConfigureAwait(false); await using var _ = storage.ConfigureAwait(false); using (var stream = SerializableBytes.CreateWritableStream()) @@ -150,7 +167,9 @@ internal partial class AbstractSyntaxIndex : IObjectWritable } stream.Position = 0; - return await storage.WriteStreamAsync(document, s_persistenceName, stream, this.Checksum, cancellationToken).ConfigureAwait(false); + + var documentKey = DocumentKey.ToDocumentKey(ProjectKey.ToProjectKey(solutionKey, project), document); + return await storage.WriteStreamAsync(documentKey, s_persistenceName, stream, this.Checksum, cancellationToken).ConfigureAwait(false); } } catch (Exception e) when (IOUtilities.IsNormalIOException(e)) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 5f7cacf27d4db..17940e9387814 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -158,9 +158,8 @@ private static string GetMetadataNameWithoutBackticks(MetadataReader reader, Str // CreateMetadataSymbolTreeInfoAsync var asyncLazy = s_peReferenceToInfo.GetValue( reference, - id => new AsyncLazy( - c => CreateMetadataSymbolTreeInfoAsync(services, solutionKey, reference, checksum, c), - cacheResult: true)); + id => AsyncLazy.Create( + c => CreateMetadataSymbolTreeInfoAsync(services, solutionKey, reference, checksum, c))); return await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); } @@ -178,15 +177,14 @@ private static string GetMetadataNameWithoutBackticks(MetadataReader reader, Str var asyncLazy = s_metadataIdToSymbolTreeInfo.GetValue( metadataId, - metadataId => new AsyncLazy( + metadataId => AsyncLazy.Create( cancellationToken => LoadOrCreateAsync( services, solutionKey, checksum, createAsync: checksum => new ValueTask(new MetadataInfoCreator(checksum, GetMetadataNoThrow(reference)).Create()), keySuffix: GetMetadataKeySuffix(reference), - cancellationToken), - cacheResult: true)); + cancellationToken))); var metadataIdSymbolTreeInfo = await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 2763de2b6540a..8f508f90f6f63 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -72,7 +72,7 @@ private static string GetSourceKeySuffix(Project project) public static Task GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) { var lazy = s_projectToSourceChecksum.GetValue( - project.State, static p => new AsyncLazy(c => ComputeSourceSymbolsChecksumAsync(p, c), cacheResult: true)); + project.State, static p => AsyncLazy.Create(c => ComputeSourceSymbolsChecksumAsync(p, c))); return lazy.GetValueAsync(cancellationToken); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs index e5b837a1c7d32..2658bd8b2f2b3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Storage; namespace Microsoft.CodeAnalysis.FindSymbols { @@ -31,13 +31,21 @@ internal sealed partial class SyntaxTreeIndex : AbstractSyntaxIndex GetRequiredIndexAsync(Document document, CancellationToken cancellationToken) - => GetRequiredIndexAsync(document, ReadIndex, CreateIndex, cancellationToken); + => GetRequiredIndexAsync(SolutionKey.ToSolutionKey(document.Project.Solution), document.Project.State, (DocumentState)document.State, cancellationToken); + + public static ValueTask GetRequiredIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, CancellationToken cancellationToken) + => GetRequiredIndexAsync(solutionKey, project, document, ReadIndex, CreateIndex, cancellationToken); public static ValueTask GetIndexAsync(Document document, CancellationToken cancellationToken) - => GetIndexAsync(document, loadOnly: false, cancellationToken); + => GetIndexAsync(SolutionKey.ToSolutionKey(document.Project.Solution), document.Project.State, (DocumentState)document.State, cancellationToken); + + public static ValueTask GetIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, CancellationToken cancellationToken) + => GetIndexAsync(solutionKey, project, document, loadOnly: false, cancellationToken); - [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] public static ValueTask GetIndexAsync(Document document, bool loadOnly, CancellationToken cancellationToken) - => GetIndexAsync(document, loadOnly, ReadIndex, CreateIndex, cancellationToken); + => GetIndexAsync(SolutionKey.ToSolutionKey(document.Project.Solution), document.Project.State, (DocumentState)document.State, loadOnly, cancellationToken); + + public static ValueTask GetIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, bool loadOnly, CancellationToken cancellationToken) + => GetIndexAsync(solutionKey, project, document, loadOnly, ReadIndex, CreateIndex, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 5cf6b3b7a9ad2..8e80cd2aba2ee 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -37,12 +37,12 @@ internal sealed partial class SyntaxTreeIndex /// this string table. The table will have already served its purpose at that point and /// doesn't need to be kept around further. /// - private static readonly ConditionalWeakTable s_projectStringTable = new(); + private static readonly ConditionalWeakTable s_projectStringTable = new(); private static SyntaxTreeIndex CreateIndex( - Document document, SyntaxNode root, Checksum checksum, CancellationToken _) + ProjectState project, SyntaxNode root, Checksum checksum, CancellationToken _) { - var syntaxFacts = document.GetRequiredLanguageService(); + var syntaxFacts = project.LanguageServices.GetRequiredService(); var ignoreCase = !syntaxFacts.IsCaseSensitive; var isCaseSensitive = !ignoreCase; @@ -251,7 +251,7 @@ private static bool IsGlobalSuppressMessageAttribute(ISyntaxFactsService syntaxF } } - public static StringTable GetStringTable(Project project) + public static StringTable GetStringTable(ProjectState project) => s_projectStringTable.GetValue(project, static _ => StringTable.GetInstance()); private static void GetIdentifierSet(bool ignoreCase, out HashSet identifiers, out HashSet escapedIdentifiers) diff --git a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/IDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/IDeclaredSymbolInfoFactoryService.cs index 355489b36ef6e..912861d312dfd 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/IDeclaredSymbolInfoFactoryService.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/IDeclaredSymbolInfoFactoryService.cs @@ -13,6 +13,6 @@ internal interface IDeclaredSymbolInfoFactoryService : ILanguageService { // `rootNamespace` is required for VB projects that has non-global namespace as root namespace, // otherwise we would not be able to get correct data from syntax. - void AddDeclaredSymbolInfos(Document document, SyntaxNode root, ArrayBuilder declaredSymbolInfos, Dictionary> extensionMethodInfo, CancellationToken cancellationToken); + void AddDeclaredSymbolInfos(ProjectState project, SyntaxNode root, ArrayBuilder declaredSymbolInfos, Dictionary> extensionMethodInfo, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex.cs index 7882e6d42b196..6420811a4f547 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols @@ -44,13 +45,21 @@ public bool ContainsExtensionMethod => _extensionMethodInfo.ContainsExtensionMethod; public static ValueTask GetRequiredIndexAsync(Document document, CancellationToken cancellationToken) - => GetRequiredIndexAsync(document, ReadIndex, CreateIndex, cancellationToken); + => GetRequiredIndexAsync(SolutionKey.ToSolutionKey(document.Project.Solution), document.Project.State, (DocumentState)document.State, cancellationToken); + + public static ValueTask GetRequiredIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, CancellationToken cancellationToken) + => GetRequiredIndexAsync(solutionKey, project, document, ReadIndex, CreateIndex, cancellationToken); public static ValueTask GetIndexAsync(Document document, CancellationToken cancellationToken) - => GetIndexAsync(document, ReadIndex, CreateIndex, cancellationToken); + => GetIndexAsync(SolutionKey.ToSolutionKey(document.Project.Solution), document.Project.State, (DocumentState)document.State, cancellationToken); + + public static ValueTask GetIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, CancellationToken cancellationToken) + => GetIndexAsync(solutionKey, project, document, ReadIndex, CreateIndex, cancellationToken); - [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] public static ValueTask GetIndexAsync(Document document, bool loadOnly, CancellationToken cancellationToken) - => GetIndexAsync(document, loadOnly, ReadIndex, CreateIndex, cancellationToken); + => GetIndexAsync(SolutionKey.ToSolutionKey(document.Project.Solution), document.Project.State, (DocumentState)document.State, loadOnly, cancellationToken); + + public static ValueTask GetIndexAsync(SolutionKey solutionKey, ProjectState project, DocumentState document, bool loadOnly, CancellationToken cancellationToken) + => GetIndexAsync(solutionKey, project, document, loadOnly, ReadIndex, CreateIndex, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex_Create.cs index aeed7c2412e1b..c6b68a506f31b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/TopLevelSyntaxTree/TopLevelSyntaxTreeIndex_Create.cs @@ -15,16 +15,16 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal sealed partial class TopLevelSyntaxTreeIndex { private static TopLevelSyntaxTreeIndex CreateIndex( - Document document, SyntaxNode root, Checksum checksum, CancellationToken cancellationToken) + ProjectState project, SyntaxNode root, Checksum checksum, CancellationToken cancellationToken) { - var infoFactory = document.GetRequiredLanguageService(); + var infoFactory = project.LanguageServices.GetRequiredService(); using var _1 = ArrayBuilder.GetInstance(out var declaredSymbolInfos); using var _2 = PooledDictionary>.GetInstance(out var extensionMethodInfo); try { infoFactory.AddDeclaredSymbolInfos( - document, root, declaredSymbolInfos, extensionMethodInfo, cancellationToken); + project, root, declaredSymbolInfos, extensionMethodInfo, cancellationToken); return new TopLevelSyntaxTreeIndex( checksum, diff --git a/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs index 0501cb472240c..24462ef3906ef 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/DeclaredSymbolFactoryService/AbstractDeclaredSymbolInfoFactoryService.cs @@ -150,13 +150,12 @@ protected static void Intern(StringTable stringTable, ArrayBuilder build } public void AddDeclaredSymbolInfos( - Document document, + ProjectState project, SyntaxNode root, ArrayBuilder declaredSymbolInfos, Dictionary> extensionMethodInfo, CancellationToken cancellationToken) { - var project = document.Project; var stringTable = SyntaxTreeIndex.GetStringTable(project); var rootNamespace = this.GetRootNamespace(project.CompilationOptions!); diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 80b40f2ef9610..27fa7e51c329c 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis true - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 $(DefineConstants);WORKSPACE true full @@ -49,6 +49,7 @@ + @@ -107,9 +108,12 @@ + + + diff --git a/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs b/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs index da4f39eec73c7..eb12898fd5f53 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs @@ -2,12 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Globalization; using System.Linq; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; namespace Microsoft.CodeAnalysis.PatternMatching @@ -21,9 +18,10 @@ private sealed partial class ContainerPatternMatcher : PatternMatcher public ContainerPatternMatcher( string[] patternParts, char[] containerSplitCharacters, - CultureInfo culture, + bool includeMatchedSpans, + CultureInfo? culture, bool allowFuzzyMatching = false) - : base(false, culture, allowFuzzyMatching) + : base(includeMatchedSpans, culture, allowFuzzyMatching) { _containerSplitCharacters = containerSplitCharacters; @@ -44,7 +42,7 @@ public override void Dispose() } } - public override bool AddMatches(string container, ref TemporaryArray matches) + public override bool AddMatches(string? container, ref TemporaryArray matches) { if (SkipMatch(container)) { diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs index 36c33c465e7be..67153d122d0d2 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared; @@ -46,7 +47,7 @@ internal abstract partial class PatternMatcher : IDisposable /// Whether or not close matches should count as matches. protected PatternMatcher( bool includeMatchedSpans, - CultureInfo culture, + CultureInfo? culture, bool allowFuzzyMatching = false) { culture ??= CultureInfo.CurrentCulture; @@ -74,21 +75,23 @@ public virtual void Dispose() public static PatternMatcher CreateContainerPatternMatcher( string[] patternParts, char[] containerSplitCharacters, + bool includeMatchedSpans = false, CultureInfo? culture = null, bool allowFuzzyMatching = false) { return new ContainerPatternMatcher( - patternParts, containerSplitCharacters, culture, allowFuzzyMatching); + patternParts, containerSplitCharacters, includeMatchedSpans, culture, allowFuzzyMatching); } public static PatternMatcher CreateDotSeparatedContainerMatcher( string pattern, + bool includeMatchedSpans = false, CultureInfo? culture = null, bool allowFuzzyMatching = false) { return CreateContainerPatternMatcher( pattern.Split(s_dotCharacterArray, StringSplitOptions.RemoveEmptyEntries), - s_dotCharacterArray, culture, allowFuzzyMatching); + s_dotCharacterArray, includeMatchedSpans, culture, allowFuzzyMatching); } internal static (string name, string? containerOpt) GetNameAndContainer(string pattern) @@ -102,7 +105,7 @@ internal static (string name, string? containerOpt) GetNameAndContainer(string p public abstract bool AddMatches(string? candidate, ref TemporaryArray matches); - private bool SkipMatch(string? candidate) + private bool SkipMatch([NotNullWhen(false)] string? candidate) => _invalidPattern || string.IsNullOrWhiteSpace(candidate); private static bool ContainsUpperCaseLetter(string pattern) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISolutionExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISolutionExtensions.cs index b917eb25f2338..cc8a8bf5b29b5 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISolutionExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISolutionExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; @@ -38,6 +39,20 @@ internal static partial class ISolutionExtensions public static TextDocumentKind? GetDocumentKind(this Solution solution, DocumentId documentId) => solution.GetTextDocument(documentId)?.Kind; + internal static TextDocument? GetTextDocumentForLocation(this Solution solution, Location location) + { + switch (location.Kind) + { + case LocationKind.SourceFile: + return solution.GetDocument(location.SourceTree); + case LocationKind.ExternalFile: + var documentId = solution.GetDocumentIdsWithFilePath(location.GetLineSpan().Path).FirstOrDefault(); + return solution.GetTextDocument(documentId); + default: + return null; + } + } + public static Solution WithTextDocumentText(this Solution solution, DocumentId documentId, SourceText text, PreservationMode mode = PreservationMode.PreserveIdentity) { var documentKind = solution.GetDocumentKind(documentId); diff --git a/src/Workspaces/Core/Portable/Workspace/IWorkspaceConfigurationService.cs b/src/Workspaces/Core/Portable/Workspace/IWorkspaceConfigurationService.cs index 3e93eef000941..a966c424d5dbb 100644 --- a/src/Workspaces/Core/Portable/Workspace/IWorkspaceConfigurationService.cs +++ b/src/Workspaces/Core/Portable/Workspace/IWorkspaceConfigurationService.cs @@ -40,7 +40,9 @@ public DefaultWorkspaceConfigurationService() internal readonly record struct WorkspaceConfigurationOptions( [property: DataMember(Order = 0)] StorageDatabase CacheStorage = StorageDatabase.SQLite, [property: DataMember(Order = 1)] bool EnableOpeningSourceGeneratedFiles = false, - [property: DataMember(Order = 2)] bool DisableSharedSyntaxTrees = false) + [property: DataMember(Order = 2)] bool DisableSharedSyntaxTrees = false, + [property: DataMember(Order = 3)] bool DeferCreatingRecoverableText = false, + [property: DataMember(Order = 4)] bool DisableRecoverableText = false) { public WorkspaceConfigurationOptions() : this(CacheStorage: StorageDatabase.SQLite) @@ -56,6 +58,8 @@ public WorkspaceConfigurationOptions() public static readonly WorkspaceConfigurationOptions RemoteDefault = new( CacheStorage: StorageDatabase.None, EnableOpeningSourceGeneratedFiles: false, - DisableSharedSyntaxTrees: false); + DisableSharedSyntaxTrees: false, + DeferCreatingRecoverableText: false, + DisableRecoverableText: false); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs index ccb3d66b4c148..04be1ead5dc5f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/AnalyzerConfigDocumentState.cs @@ -40,8 +40,7 @@ private ValueSource CreateAnalyzerConfigValueSource() { return new AsyncLazy( asynchronousComputeFunction: async cancellationToken => AnalyzerConfig.Parse(await GetTextAsync(cancellationToken).ConfigureAwait(false), FilePath), - synchronousComputeFunction: cancellationToken => AnalyzerConfig.Parse(GetTextSynchronously(cancellationToken), FilePath), - cacheResult: true); + synchronousComputeFunction: cancellationToken => AnalyzerConfig.Parse(GetTextSynchronously(cancellationToken), FilePath)); } public AnalyzerConfig GetAnalyzerConfig(CancellationToken cancellationToken) => _analyzerConfigValueSource.GetValue(cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index bcbed0a59fa0e..5dcf4f0a2367e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -13,15 +13,25 @@ using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; using System.Diagnostics; +using System.Runtime.CompilerServices; namespace Microsoft.CodeAnalysis { // various factory methods. all these are just helper methods internal partial class Checksum { + // https://github.com/dotnet/runtime/blob/f2db6d6093c54e5eeb9db2d8dcbe15b2db92ad8c/src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/SHA256.cs#L18-L19 + private const int SHA256HashSizeBytes = 256 / 8; + private static readonly ObjectPool s_incrementalHashPool = new(() => IncrementalHash.CreateHash(HashAlgorithmName.SHA256), size: 20); + // Dedicated pools for the byte[]s we use to create checksums from two or three existing checksums. Sized to + // exactly the space needed to splat the existing checksum data into the array and then hash it. + + private static readonly ObjectPool s_twoChecksumByteArrayPool = new(() => new byte[HashSize * 2]); + private static readonly ObjectPool s_threeChecksumByteArrayPool = new(() => new byte[HashSize * 3]); + public static Checksum Create(IEnumerable values) { using var pooledHash = s_incrementalHashPool.GetPooledObject(); @@ -98,33 +108,77 @@ public static Checksum Create(IObjectWritable @object) public static Checksum Create(Checksum checksum1, Checksum checksum2) { - using var stream = SerializableBytes.CreateWritableStream(); +#if NET + return CreateUsingSpans(checksum1, checksum2); +#else + return CreateUsingByteArrays(checksum1, checksum2); +#endif + } - using (var writer = new ObjectWriter(stream, leaveOpen: true)) - { - checksum1.WriteTo(writer); - checksum2.WriteTo(writer); - } + public static Checksum Create(Checksum checksum1, Checksum checksum2, Checksum checksum3) + { +#if NET + return CreateUsingSpans(checksum1, checksum2, checksum3); +#else + return CreateUsingByteArrays(checksum1, checksum2, checksum3); +#endif + } - stream.Position = 0; - return Create(stream); + private static Checksum CreateUsingByteArrays(Checksum checksum1, Checksum checksum2) + { + using var hash = s_incrementalHashPool.GetPooledObject(); + using var bytes = s_twoChecksumByteArrayPool.GetPooledObject(); + + var bytesSpan = bytes.Object.AsSpan(); + checksum1.WriteTo(bytesSpan); + checksum2.WriteTo(bytesSpan.Slice(HashSize)); + + hash.Object.AppendData(bytes.Object); + + return From(hash.Object.GetHashAndReset()); } - public static Checksum Create(Checksum checksum1, Checksum checksum2, Checksum checksum3) + private static Checksum CreateUsingByteArrays(Checksum checksum1, Checksum checksum2, Checksum checksum3) { - using var stream = SerializableBytes.CreateWritableStream(); + using var hash = s_incrementalHashPool.GetPooledObject(); + using var bytes = s_threeChecksumByteArrayPool.GetPooledObject(); - using (var writer = new ObjectWriter(stream, leaveOpen: true)) - { - checksum1.WriteTo(writer); - checksum2.WriteTo(writer); - checksum3.WriteTo(writer); - } + var bytesSpan = bytes.Object.AsSpan(); + checksum1.WriteTo(bytesSpan); + checksum2.WriteTo(bytesSpan.Slice(HashSize)); + checksum3.WriteTo(bytesSpan.Slice(2 * HashSize)); - stream.Position = 0; - return Create(stream); + hash.Object.AppendData(bytes.Object); + + return From(hash.Object.GetHashAndReset()); + } + +#if NET + + // Optimized helpers that do not need to allocate any arrays to combine hashes. + + private static Checksum CreateUsingSpans(Checksum checksum1, Checksum checksum2) + { + Span checksums = stackalloc HashData[] { checksum1._checksum, checksum2._checksum }; + Span hashResultSpan = stackalloc byte[SHA256HashSizeBytes]; + + SHA256.HashData(MemoryMarshal.AsBytes(checksums), hashResultSpan); + + return From(hashResultSpan); } + private static Checksum CreateUsingSpans(Checksum checksum1, Checksum checksum2, Checksum checksum3) + { + Span checksums = stackalloc HashData[] { checksum1._checksum, checksum2._checksum, checksum3._checksum }; + Span hashResultSpan = stackalloc byte[SHA256HashSizeBytes]; + + SHA256.HashData(MemoryMarshal.AsBytes(checksums), hashResultSpan); + + return From(hashResultSpan); + } + +#endif + public static Checksum Create(IEnumerable checksums) { using var stream = SerializableBytes.CreateWritableStream(); @@ -197,5 +251,24 @@ private static void AppendData(IncrementalHash hash, byte[] buffer, string value index += toCopy; } } + + public static class TestAccessor + { + public static Checksum CreateUsingByteArrays(Checksum checksum1, Checksum checksum2) + => Checksum.CreateUsingByteArrays(checksum1, checksum2); + + public static Checksum CreateUsingByteArrays(Checksum checksum1, Checksum checksum2, Checksum checksum3) + => Checksum.CreateUsingByteArrays(checksum1, checksum2, checksum3); + +#if NET + + public static Checksum CreateUsingSpans(Checksum checksum1, Checksum checksum2) + => Checksum.CreateUsingSpans(checksum1, checksum2); + + public static Checksum CreateUsingSpans(Checksum checksum1, Checksum checksum2, Checksum checksum3) + => Checksum.CreateUsingSpans(checksum1, checksum2, checksum3); + +#endif + } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs index 78754519210e5..1b7268ac8f73b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -46,9 +45,12 @@ public Task GetValueAsync(LoadTextOptions options, CancellationT public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) => TryGetValue(out value); - //public bool TryGetTextVersion(out VersionStamp version) - //{ - // version = Value.Version; - // return true; - //} + public bool TryGetVersion(LoadTextOptions options, out VersionStamp version) + { + version = _value.Version; + return true; + } + + public ValueTask GetVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => new(_value.Version); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index b7641f11507b5..dd50912f6dd0e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -488,11 +488,11 @@ public Task GetOptionsAsync(CancellationToken cancellationTok private void InitializeCachedOptions(OptionSet solutionOptions) { - var newAsyncLazy = new AsyncLazy(async cancellationToken => + var newAsyncLazy = AsyncLazy.Create(async cancellationToken => { var options = await GetAnalyzerConfigOptionsAsync(cancellationToken).ConfigureAwait(false); return new DocumentOptionSet(options, solutionOptions, Project.Language); - }, cacheResult: true); + }); Interlocked.CompareExchange(ref _cachedOptions, newAsyncLazy, comparand: null); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 155d4a326ae09..c4d02b1090b24 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -107,8 +107,7 @@ public bool IsGenerated { return new AsyncLazy( c => FullyParseTreeAsync(newTextSource, loadTextOptions, filePath, options, languageServices, mode, c), - c => FullyParseTree(newTextSource, loadTextOptions, filePath, options, languageServices, mode, c), - cacheResult: true); + c => FullyParseTree(newTextSource, loadTextOptions, filePath, options, languageServices, mode, c)); } private static async Task FullyParseTreeAsync( @@ -170,8 +169,7 @@ public bool IsGenerated { return new AsyncLazy( c => IncrementallyParseTreeAsync(oldTreeSource, newTextSource, loadTextOptions, c), - c => IncrementallyParseTree(oldTreeSource, newTextSource, loadTextOptions, c), - cacheResult: true); + c => IncrementallyParseTree(oldTreeSource, newTextSource, loadTextOptions, c)); } private static async Task IncrementallyParseTreeAsync( @@ -545,10 +543,7 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) // its okay to use a strong cached AsyncLazy here because the compiler layer SyntaxTree will also keep the text alive once its built. var lazyTextAndVersion = new TreeTextSource( - new AsyncLazy( - tree.GetTextAsync, - tree.GetText, - cacheResult: true), + new AsyncLazy(tree.GetTextAsync, tree.GetText), textVersion); return (lazyTextAndVersion, new TreeAndVersion(tree, treeVersion)); @@ -649,7 +644,7 @@ public bool TryGetTopLevelChangeTextVersion(out VersionStamp version) } } - public override async Task GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken) + public override async ValueTask GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken) { if (_treeSource == null) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_LinkedFileReuse.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_LinkedFileReuse.cs index 6a6ae7701b199..a7402074d0f6f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_LinkedFileReuse.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_LinkedFileReuse.cs @@ -75,8 +75,7 @@ public DocumentState UpdateTextAndTreeContents(ITextAndVersionSource siblingText { return new AsyncLazy( cancellationToken => TryReuseSiblingTreeAsync(filePath, languageServices, loadTextOptions, parseOptions, treeSource, siblingTextSource, siblingTreeSource, cancellationToken), - cancellationToken => TryReuseSiblingTree(filePath, languageServices, loadTextOptions, parseOptions, treeSource, siblingTextSource, siblingTreeSource, cancellationToken), - cacheResult: true); + cancellationToken => TryReuseSiblingTree(filePath, languageServices, loadTextOptions, parseOptions, treeSource, siblingTextSource, siblingTreeSource, cancellationToken)); } static bool TryReuseSiblingRoot( diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs index 16bc4f27b467c..941cb267fc1de 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState_TreeTextSource.cs @@ -15,7 +15,7 @@ internal partial class DocumentState /// /// A source for constructed from an syntax tree. /// - private sealed class TreeTextSource : ITextAndVersionSource, ITextVersionable + private sealed class TreeTextSource : ITextAndVersionSource { private readonly ValueSource _textSource; private readonly VersionStamp _version; @@ -55,11 +55,14 @@ public bool TryGetValue(LoadTextOptions options, [NotNullWhen(true)] out TextAnd } } - public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) + public bool TryGetVersion(LoadTextOptions options, out VersionStamp version) { version = _version; return version != default; } + + public ValueTask GetVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + => new(_version); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs index c6b8eb49fb6d8..ab0bc21554dde 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs @@ -19,4 +19,12 @@ internal interface ITextAndVersionSource bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value); TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken); Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken); + + bool TryGetVersion(LoadTextOptions options, out VersionStamp version); + + /// + /// Retrieves just the version information from this instance. Cheaper than when only + /// the version is needed, and avoiding loading the text is desirable. + /// + ValueTask GetVersionAsync(LoadTextOptions options, CancellationToken cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.cs deleted file mode 100644 index 3d8f25fca28e6..0000000000000 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ITextVersionable.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 -{ - internal interface ITextVersionable - { - bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version); - } -} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs index ba38c0915c6e8..94b3467cdf651 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -16,12 +15,27 @@ internal sealed class LoadableTextAndVersionSource : ITextAndVersionSource private sealed class LazyValueWithOptions { public readonly LoadableTextAndVersionSource Source; - public readonly AsyncLazy LazyValue; public readonly LoadTextOptions Options; + private readonly SemaphoreSlim _gate = new(initialCount: 1); + + /// + /// Strong reference to the loaded text and version. Only held onto once computed if . is . Once held onto, this will be returned from all calls to + /// , or . Once non-null will always + /// remain non-null. + /// + private TextAndVersion? _instance; + + /// + /// Weak reference to the loaded text and version that we create whenever the value is computed. We will + /// attempt to return from this if still alive when clients call back into this. If neither this, nor are available, the value will be reloaded. Once non-null, this will always be non-null. + /// + private WeakReference? _weakInstance; + public LazyValueWithOptions(LoadableTextAndVersionSource source, LoadTextOptions options) { - LazyValue = new AsyncLazy(LoadAsync, LoadSynchronously, source.CacheResult); Source = source; Options = options; } @@ -31,6 +45,67 @@ private Task LoadAsync(CancellationToken cancellationToken) private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) => Source.Loader.LoadTextSynchronously(Options, cancellationToken); + + public bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) + { + value = _instance; + if (value != null) + return true; + + return _weakInstance?.TryGetTarget(out value) == true && value != null; + } + + public TextAndVersion GetValue(CancellationToken cancellationToken) + { + if (!TryGetValue(out var textAndVersion)) + { + using (_gate.DisposableWait(cancellationToken)) + { + if (!TryGetValue(out textAndVersion)) + { + textAndVersion = LoadSynchronously(cancellationToken); + UpdateWeakAndStrongReferences_NoLock(textAndVersion); + } + } + } + + return textAndVersion; + } + + public async Task GetValueAsync(CancellationToken cancellationToken) + { + if (!TryGetValue(out var textAndVersion)) + { + using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + { + if (!TryGetValue(out textAndVersion)) + { + textAndVersion = await LoadAsync(cancellationToken).ConfigureAwait(false); + UpdateWeakAndStrongReferences_NoLock(textAndVersion); + } + } + } + + return textAndVersion; + } + + private void UpdateWeakAndStrongReferences_NoLock(TextAndVersion textAndVersion) + { + Contract.ThrowIfTrue(_gate.CurrentCount != 0); + + if (this.Source.CacheResult) + { + // if our source wants us to hold onto the value strongly, do so. No need to involve the weak-refs as + // this will now hold onto the value forever. + _instance = textAndVersion; + } + else + { + // Update the weak ref, so we can return the same instance if anything else is holding onto it. + _weakInstance ??= new WeakReference(textAndVersion); + _weakInstance.SetTarget(textAndVersion); + } + } } public readonly TextLoader Loader; @@ -47,7 +122,7 @@ public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) public bool CanReloadText => Loader.CanReloadText; - private AsyncLazy GetLazyValue(LoadTextOptions options) + private LazyValueWithOptions GetLazyValue(LoadTextOptions options) { var lazy = _lazyValue; @@ -57,7 +132,7 @@ private AsyncLazy GetLazyValue(LoadTextOptions options) _lazyValue = lazy = new LazyValueWithOptions(this, options); } - return lazy.LazyValue; + return lazy; } public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) @@ -68,4 +143,22 @@ public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out Text public Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) => GetLazyValue(options).GetValueAsync(cancellationToken); + + public bool TryGetVersion(LoadTextOptions options, out VersionStamp version) + { + if (!TryGetValue(options, out var value)) + { + version = default; + return false; + } + + version = value.Version; + return true; + } + + public async ValueTask GetVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + { + var value = await GetValueAsync(options, cancellationToken).ConfigureAwait(false); + return value.Version; + } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs index cf16b22f5fcb4..4593d8b9c5c70 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs @@ -128,6 +128,17 @@ internal ProjectDependencyGraph WithProjectReferences(ProjectId projectId, IRead { Contract.ThrowIfFalse(_projectIds.Contains(projectId)); + if (!_referencesMap.ContainsKey(projectId)) + { + // This project doesn't have any references currently, so we delegate to WithAdditionalProjectReferences + return WithAdditionalProjectReferences(projectId, projectReferences); + } + else if (projectReferences.Count == 0) + { + // We are removing all project references; do so directly + return WithAllProjectReferencesRemoved(projectId); + } + // This method we can't optimize very well: changing project references arbitrarily could invalidate pretty much anything. // The only thing we can reuse is our actual map of project references for all the other projects, so we'll do that. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_RemoveAllProjectReferences.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_RemoveAllProjectReferences.cs new file mode 100644 index 0000000000000..22deeab440b73 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_RemoveAllProjectReferences.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + public partial class ProjectDependencyGraph + { + internal ProjectDependencyGraph WithAllProjectReferencesRemoved(ProjectId projectId) + { + Contract.ThrowIfFalse(_projectIds.Contains(projectId)); + + if (!_referencesMap.TryGetValue(projectId, out var referencedProjectIds)) + return this; + + // Removing a project reference doesn't change the set of projects + var projectIds = _projectIds; + + // Incrementally update the graph + var referencesMap = ComputeNewReferencesMapForRemovedAllProjectReferences(_referencesMap, projectId); + var reverseReferencesMap = ComputeNewReverseReferencesMapForRemovedAllProjectReferences(_lazyReverseReferencesMap, projectId, referencedProjectIds); + var transitiveReferencesMap = ComputeNewTransitiveReferencesMapForRemovedAllProjectReferences(_transitiveReferencesMap, projectId, referencedProjectIds); + var reverseTransitiveReferencesMap = ComputeNewReverseTransitiveReferencesMapForRemovedAllProjectReferences(_reverseTransitiveReferencesMap, projectId); + + return new ProjectDependencyGraph( + projectIds, + referencesMap, + reverseReferencesMap, + transitiveReferencesMap, + reverseTransitiveReferencesMap, + topologicallySortedProjects: default, + dependencySets: default); + } + + private static ImmutableDictionary> ComputeNewReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary> existingForwardReferencesMap, + ProjectId projectId) + { + // Projects with no references do not have an entry in the forward references map + return existingForwardReferencesMap.Remove(projectId); + } + + /// + /// Computes a new for the removal of all project references from a + /// project. + /// + /// The prior to the removal, + /// or if the reverse references map was not computed for the prior graph. + /// The project ID from which a project reference is being removed. + /// The targets of the project references which are being removed. + /// The updated (complete) reverse references map, or if the reverse references + /// map could not be incrementally updated. + private static ImmutableDictionary>? ComputeNewReverseReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary>? existingReverseReferencesMap, + ProjectId projectId, + ImmutableHashSet referencedProjectIds) + { + if (existingReverseReferencesMap is null) + { + return null; + } + + var builder = existingReverseReferencesMap.ToBuilder(); + foreach (var referencedProjectId in referencedProjectIds) + { + builder.MultiRemove(referencedProjectId, projectId); + } + + return builder.ToImmutable(); + } + + private static ImmutableDictionary> ComputeNewTransitiveReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary> existingTransitiveReferencesMap, + ProjectId projectId, + ImmutableHashSet referencedProjectIds) + { + var builder = existingTransitiveReferencesMap.ToBuilder(); + + // Invalidate the transitive references from every project referencing the changed project (transitively) + foreach (var (project, references) in existingTransitiveReferencesMap) + { + if (!references.Contains(projectId)) + { + // This is the forward-references-equivalent of the optimization in the update of reverse transitive + // references. + continue; + } + + Debug.Assert(references.IsSupersetOf(referencedProjectIds)); + builder.Remove(project); + } + + // Invalidate the transitive references from the changed project + builder.Remove(projectId); + + return builder.ToImmutable(); + } + + private static ImmutableDictionary> ComputeNewReverseTransitiveReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary> existingReverseTransitiveReferencesMap, + ProjectId projectId) + { + var builder = existingReverseTransitiveReferencesMap.ToBuilder(); + + // Invalidate the transitive reverse references from every project previously referenced by the original + // project (transitively). + foreach (var (project, references) in existingReverseTransitiveReferencesMap) + { + if (!references.Contains(projectId)) + { + continue; + } + + builder.Remove(project); + } + + return builder.ToImmutable(); + } + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index e8415013fe8f4..e1f3658a71a26 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -88,7 +88,7 @@ internal partial class ProjectState // the info has changed by DocumentState. _projectInfo = ClearAllDocumentsFromProjectInfo(projectInfo); - _lazyChecksums = new AsyncLazy(ComputeChecksumsAsync, cacheResult: true); + _lazyChecksums = AsyncLazy.Create(ComputeChecksumsAsync); } public ProjectState(LanguageServices languageServices, ProjectInfo projectInfo) @@ -119,8 +119,8 @@ public ProjectState(LanguageServices languageServices, ProjectInfo projectInfo) DocumentStates = new TextDocumentStates(projectInfoFixed.Documents, info => CreateDocument(info, parseOptions, loadTextOptions)); AdditionalDocumentStates = new TextDocumentStates(projectInfoFixed.AdditionalDocuments, info => new AdditionalDocumentState(languageServices.SolutionServices, info, loadTextOptions)); - _lazyLatestDocumentVersion = new AsyncLazy(c => ComputeLatestDocumentVersionAsync(DocumentStates, AdditionalDocumentStates, c), cacheResult: true); - _lazyLatestDocumentTopLevelChangeVersion = new AsyncLazy(c => ComputeLatestDocumentTopLevelChangeVersionAsync(DocumentStates, AdditionalDocumentStates, c), cacheResult: true); + _lazyLatestDocumentVersion = AsyncLazy.Create(c => ComputeLatestDocumentVersionAsync(DocumentStates, AdditionalDocumentStates, c)); + _lazyLatestDocumentTopLevelChangeVersion = AsyncLazy.Create(c => ComputeLatestDocumentTopLevelChangeVersionAsync(DocumentStates, AdditionalDocumentStates, c)); // ownership of information on document has moved to project state. clear out documentInfo the state is // holding on. otherwise, these information will be held onto unnecessarily by projectInfo even after @@ -128,7 +128,7 @@ public ProjectState(LanguageServices languageServices, ProjectInfo projectInfo) // we hold onto the info so that we don't need to duplicate all information info already has in the state _projectInfo = ClearAllDocumentsFromProjectInfo(projectInfoFixed); - _lazyChecksums = new AsyncLazy(ComputeChecksumsAsync, cacheResult: true); + _lazyChecksums = AsyncLazy.Create(ComputeChecksumsAsync); } private static ProjectInfo ClearAllDocumentsFromProjectInfo(ProjectInfo projectInfo) @@ -195,11 +195,11 @@ private static async Task ComputeLatestDocumentVersionAsync(TextDo { if (_lazyLatestDocumentTopLevelChangeVersion.TryGetValue(out var oldVersion)) { - return new AsyncLazy(c => ComputeTopLevelChangeTextVersionAsync(oldVersion, newDocument, c), cacheResult: true); + return AsyncLazy.Create(c => ComputeTopLevelChangeTextVersionAsync(oldVersion, newDocument, c)); } else { - return new AsyncLazy(c => ComputeLatestDocumentTopLevelChangeVersionAsync(newDocumentStates, newAdditionalDocumentStates, c), cacheResult: true); + return AsyncLazy.Create(c => ComputeLatestDocumentTopLevelChangeVersionAsync(newDocumentStates, newAdditionalDocumentStates, c)); } } @@ -490,8 +490,7 @@ private static ValueSource ComputeAnalyzerConfigOpti { var analyzerConfigs = analyzerConfigDocumentStates.SelectAsArray(a => a.GetAnalyzerConfig(cancellationToken)); return new AnalyzerConfigOptionsCache(AnalyzerConfigSet.Create(analyzerConfigs)); - }, - cacheResult: true); + }); } private readonly struct AnalyzerConfigOptionsCache @@ -953,13 +952,13 @@ public ProjectState UpdateDocumentsOrder(ImmutableList documentIds) } dependentDocumentVersion = recalculateDocumentVersion - ? new AsyncLazy(c => ComputeLatestDocumentVersionAsync(newDocumentStates, newAdditionalDocumentStates, c), cacheResult: true) + ? AsyncLazy.Create(c => ComputeLatestDocumentVersionAsync(newDocumentStates, newAdditionalDocumentStates, c)) : contentChanged - ? new AsyncLazy(newDocument.GetTextVersionAsync, cacheResult: true) + ? AsyncLazy.Create(newDocument.GetTextVersionAsync) : _lazyLatestDocumentVersion; dependentSemanticVersion = recalculateSemanticVersion - ? new AsyncLazy(c => ComputeLatestDocumentTopLevelChangeVersionAsync(newDocumentStates, newAdditionalDocumentStates, c), cacheResult: true) + ? AsyncLazy.Create(c => ComputeLatestDocumentTopLevelChangeVersionAsync(newDocumentStates, newAdditionalDocumentStates, c)) : contentChanged ? CreateLazyLatestDocumentTopLevelChangeVersion(newDocument, newDocumentStates, newAdditionalDocumentStates) : _lazyLatestDocumentTopLevelChangeVersion; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs index ee5645a5bb545..11ad671dd6bd4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -16,7 +15,7 @@ namespace Microsoft.CodeAnalysis /// /// A recoverable TextAndVersion source that saves its text to temporary storage. /// - internal sealed class RecoverableTextAndVersion : ITextVersionable, ITextAndVersionSource + internal sealed class RecoverableTextAndVersion : ITextAndVersionSource { private readonly SolutionServices _services; @@ -58,11 +57,9 @@ private bool TryGetInitialSourceOrRecoverableText([NotNullWhen(true)] out ITextA public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) { if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) - { return source.TryGetValue(options, out value); - } - if (recoverableText.TryGetValue(out var text) && recoverableText.LoadTextOptions == options) + if (recoverableText.LoadTextOptions == options && recoverableText.TryGetValue(out var text)) { value = TextAndVersion.Create(text, recoverableText.Version, recoverableText.LoadDiagnostic); return true; @@ -72,16 +69,14 @@ public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out Text return false; } - public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) + public bool TryGetVersion(LoadTextOptions options, out VersionStamp version) { - if (_initialSourceOrRecoverableText is ITextVersionable textVersionable) - { - return textVersionable.TryGetTextVersion(options, out version); - } + if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) + return source.TryGetVersion(options, out version); - if (TryGetValue(options, out var textAndVersion)) + if (recoverableText.LoadTextOptions == options) { - version = textAndVersion.Version; + version = recoverableText.Version; return true; } @@ -89,14 +84,19 @@ public bool TryGetTextVersion(LoadTextOptions options, out VersionStamp version) return false; } - public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) + private async ValueTask GetRecoverableTextAsync( + bool useAsync, LoadTextOptions options, CancellationToken cancellationToken) { if (_initialSourceOrRecoverableText is ITextAndVersionSource source) { // replace initial source with recoverable text if it hasn't been replaced already: + var textAndVersion = useAsync + ? await source.GetValueAsync(options, cancellationToken).ConfigureAwait(false) + : source.GetValue(options, cancellationToken); + Interlocked.CompareExchange( ref _initialSourceOrRecoverableText, - value: new RecoverableText(source, source.GetValue(options, cancellationToken), options, _services), + value: new RecoverableText(source, textAndVersion, options, _services), comparand: source); } @@ -105,41 +105,41 @@ public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancel var recoverableText = (RecoverableText)_initialSourceOrRecoverableText; if (recoverableText.LoadTextOptions != options && recoverableText.InitialSource != null) { + var textAndVersion = useAsync + ? await recoverableText.InitialSource.GetValueAsync(options, cancellationToken).ConfigureAwait(false) + : recoverableText.InitialSource.GetValue(options, cancellationToken); Interlocked.Exchange( ref _initialSourceOrRecoverableText, - new RecoverableText(recoverableText.InitialSource, recoverableText.InitialSource.GetValue(options, cancellationToken), options, _services)); + new RecoverableText(recoverableText.InitialSource, textAndVersion, options, _services)); } - recoverableText = (RecoverableText)_initialSourceOrRecoverableText; - return recoverableText.ToTextAndVersion(recoverableText.GetValue(cancellationToken)); + return (RecoverableText)_initialSourceOrRecoverableText; } - public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) + public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) { - if (_initialSourceOrRecoverableText is ITextAndVersionSource source) - { - // replace initial source with recoverable text if it hasn't been replaced already: - Interlocked.CompareExchange( - ref _initialSourceOrRecoverableText, - value: new RecoverableText(source, await source.GetValueAsync(options, cancellationToken).ConfigureAwait(false), options, _services), - comparand: source); - } +#pragma warning disable CA2012 // Use ValueTasks correctly + var valueTask = GetRecoverableTextAsync(useAsync: false, options, cancellationToken); +#pragma warning restore CA2012 // Use ValueTasks correctly + Contract.ThrowIfFalse(valueTask.IsCompleted, "GetRecoverableTextAsync should have completed synchronously since we passed 'useAsync: false'"); + var recoverableText = valueTask.GetAwaiter().GetResult(); - // If we have a recoverable text but the options it was created for do not match the current options - // and the initial source supports reloading, reload and replace the recoverable text. - var recoverableText = (RecoverableText)_initialSourceOrRecoverableText; - if (recoverableText.LoadTextOptions != options && recoverableText.InitialSource != null) - { - Interlocked.Exchange( - ref _initialSourceOrRecoverableText, - new RecoverableText(recoverableText.InitialSource, await recoverableText.InitialSource.GetValueAsync(options, cancellationToken).ConfigureAwait(false), options, _services)); - } + return recoverableText.ToTextAndVersion(recoverableText.GetValue(cancellationToken)); + } - recoverableText = (RecoverableText)_initialSourceOrRecoverableText; + public async Task GetValueAsync(LoadTextOptions options, CancellationToken cancellationToken) + { + var recoverableText = await GetRecoverableTextAsync(useAsync: true, options, cancellationToken).ConfigureAwait(false); return recoverableText.ToTextAndVersion(await recoverableText.GetValueAsync(cancellationToken).ConfigureAwait(false)); } - private sealed class RecoverableText : WeaklyCachedRecoverableValueSource, ITextVersionable + public async ValueTask GetVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + { + var recoverableText = await GetRecoverableTextAsync(useAsync: true, options, cancellationToken).ConfigureAwait(false); + return recoverableText.Version; + } + + private sealed class RecoverableText : WeaklyCachedRecoverableValueSource { private readonly ITemporaryStorageServiceInternal _storageService; public readonly VersionStamp Version; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs index b8827973e4bea..271fc07a31363 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs @@ -1137,7 +1137,7 @@ public Task GetDependentVersionAsync(SolutionState solution, Cance { var tmp = solution; // temp. local to avoid a closure allocation for the fast path // note: solution is captured here, but it will go away once GetValueAsync executes. - Interlocked.CompareExchange(ref _lazyDependentVersion, new AsyncLazy(c => ComputeDependentVersionAsync(tmp, c), cacheResult: true), null); + Interlocked.CompareExchange(ref _lazyDependentVersion, AsyncLazy.Create(c => ComputeDependentVersionAsync(tmp, c)), null); } return _lazyDependentVersion.GetValueAsync(cancellationToken); @@ -1170,7 +1170,7 @@ public Task GetDependentSemanticVersionAsync(SolutionState solutio { var tmp = solution; // temp. local to avoid a closure allocation for the fast path // note: solution is captured here, but it will go away once GetValueAsync executes. - Interlocked.CompareExchange(ref _lazyDependentSemanticVersion, new AsyncLazy(c => ComputeDependentSemanticVersionAsync(tmp, c), cacheResult: true), null); + Interlocked.CompareExchange(ref _lazyDependentSemanticVersion, AsyncLazy.Create(c => ComputeDependentSemanticVersionAsync(tmp, c)), null); } return _lazyDependentSemanticVersion.GetValueAsync(cancellationToken); @@ -1201,7 +1201,7 @@ public Task GetDependentChecksumAsync(SolutionState solution, Cancella { var tmp = solution; // temp. local to avoid a closure allocation for the fast path // note: solution is captured here, but it will go away once GetValueAsync executes. - Interlocked.CompareExchange(ref _lazyDependentChecksum, new AsyncLazy(c => ComputeDependentChecksumAsync(tmp, c), cacheResult: true), null); + Interlocked.CompareExchange(ref _lazyDependentChecksum, AsyncLazy.Create(c => ComputeDependentChecksumAsync(tmp, c)), null); } return _lazyDependentChecksum.GetValueAsync(cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs index 248dccfc94b2d..7bd4c264c810f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs @@ -122,7 +122,7 @@ public Task GetDependentChecksumAsync(SolutionState solution, Cancella { var tmp = solution; // temp. local to avoid a closure allocation for the fast path // note: solution is captured here, but it will go away once GetValueAsync executes. - Interlocked.CompareExchange(ref _lazyDependentChecksum, new AsyncLazy(c => ComputeDependentChecksumAsync(tmp, c), cacheResult: true), null); + Interlocked.CompareExchange(ref _lazyDependentChecksum, AsyncLazy.Create(c => ComputeDependentChecksumAsync(tmp, c)), null); } return _lazyDependentChecksum.GetValueAsync(cancellationToken); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SkeletonReferenceCache.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SkeletonReferenceCache.cs index d95901b55b323..03115f551d7b1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SkeletonReferenceCache.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SkeletonReferenceCache.cs @@ -189,9 +189,8 @@ public SkeletonReferenceCache Clone() // concurrent requests asynchronously wait for that work to be done. var lazy = s_compilationToSkeletonSet.GetValue(compilation, - compilation => new AsyncLazy( - cancellationToken => Task.FromResult(CreateSkeletonSet(services, compilation, cancellationToken)), - cacheResult: true)); + compilation => AsyncLazy.Create( + cancellationToken => Task.FromResult(CreateSkeletonSet(services, compilation, cancellationToken)))); return await lazy.GetValueAsync(cancellationToken).ConfigureAwait(false); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index 8a1a820911530..8fa7462502662 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -105,8 +105,7 @@ internal partial class SolutionState _frozenSourceGeneratedDocumentState = frozenSourceGeneratedDocument; // when solution state is changed, we recalculate its checksum - _lazyChecksums = new AsyncLazy( - c => ComputeChecksumsAsync(projectsToInclude: null, c), cacheResult: true); + _lazyChecksums = AsyncLazy.Create(c => ComputeChecksumsAsync(projectsToInclude: null, c)); CheckInvariants(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs index 7e6cc70fc35ff..6e0a86899cb50 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState_Checksum.cs @@ -74,8 +74,7 @@ ValueSource Compute(ProjectId projectId) var projectsToInclude = new HashSet(); AddReferencedProjects(projectsToInclude, projectId); - return new AsyncLazy( - c => ComputeChecksumsAsync(projectsToInclude, c), cacheResult: true); + return AsyncLazy.Create(c => ComputeChecksumsAsync(projectsToInclude, c)); } void AddReferencedProjects(HashSet result, ProjectId projectId) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs index c2f5719101e2c..7c608cdb54d69 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs @@ -104,7 +104,7 @@ internal VersionStamp GetTextVersionSynchronously(CancellationToken cancellation /// /// Gets the version of the document's top level signature. /// - internal Task GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken = default) + internal ValueTask GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken = default) => State.GetTopLevelChangeTextVersionAsync(cancellationToken); /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index 8610e1106e4c6..21f236205b432 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -52,7 +52,7 @@ internal partial class TextDocumentState // a new AsyncLazy to compute the checksum though, and that's because there's no practical way for // the newly created TextDocumentState to have the same checksum as a previous TextDocumentState: // if we're creating a new state, it's because something changed, and we'll have to create a new checksum. - _lazyChecksums = new AsyncLazy(ComputeChecksumsAsync, cacheResult: true); + _lazyChecksums = AsyncLazy.Create(ComputeChecksumsAsync); } public TextDocumentState(SolutionServices solutionServices, DocumentInfo info, LoadTextOptions loadTextOptions) @@ -79,21 +79,38 @@ private static ITextAndVersionSource CreateStrongText(TextLoader loader) private static ITextAndVersionSource CreateRecoverableText(TextAndVersion text, LoadTextOptions loadTextOptions, SolutionServices services) { + var service = services.GetRequiredService(); + var options = service.Options; + + if (options.DisableRecoverableText) + return CreateStrongText(text); + var result = new RecoverableTextAndVersion(new ConstantTextAndVersionSource(text), services); - // This RecoverableTextAndVersion is created directly from a TextAndVersion instance. In its initial state, - // the RecoverableTextAndVersion keeps a strong reference to the initial TextAndVersion, and only - // transitions to a weak reference backed by temporary storage after the first time GetValue (or - // GetValueAsync) is called. Since we know we are creating a RecoverableTextAndVersion for the purpose of - // avoiding problematic address space overhead, we call GetValue immediately to force the object to weakly - // hold its data from the start. - result.GetValue(loadTextOptions, CancellationToken.None); + if (!options.DeferCreatingRecoverableText) + { + // This RecoverableTextAndVersion is created directly from a TextAndVersion instance. In its initial state, + // the RecoverableTextAndVersion keeps a strong reference to the initial TextAndVersion, and only + // transitions to a weak reference backed by temporary storage after the first time GetValue (or + // GetValueAsync) is called. Since we know we are creating a RecoverableTextAndVersion for the purpose of + // avoiding problematic address space overhead, we call GetValue immediately to force the object to weakly + // hold its data from the start. + result.GetValue(loadTextOptions, CancellationToken.None); + } return result; } private static ITextAndVersionSource CreateRecoverableText(TextLoader loader, SolutionServices services) - => new RecoverableTextAndVersion(new LoadableTextAndVersionSource(loader, cacheResult: false), services); + { + var service = services.GetRequiredService(); + var options = service.Options; + + if (options.DisableRecoverableText) + return CreateStrongText(loader); + + return new RecoverableTextAndVersion(new LoadableTextAndVersionSource(loader, cacheResult: false), services); + } public ITemporaryTextStorageInternal? Storage => (TextAndVersionSource as RecoverableTextAndVersion)?.Storage; @@ -113,24 +130,7 @@ public bool TryGetText([NotNullWhen(returnValue: true)] out SourceText? text) } public bool TryGetTextVersion(out VersionStamp version) - { - // try fast path first - if (this.TextAndVersionSource is ITextVersionable versionable) - { - return versionable.TryGetTextVersion(LoadTextOptions, out version); - } - - if (this.TextAndVersionSource.TryGetValue(LoadTextOptions, out var textAndVersion)) - { - version = textAndVersion.Version; - return true; - } - else - { - version = default; - return false; - } - } + => TextAndVersionSource.TryGetVersion(LoadTextOptions, out version); public bool TryGetTextAndVersion([NotNullWhen(true)] out TextAndVersion? textAndVersion) => TextAndVersionSource.TryGetValue(LoadTextOptions, out textAndVersion); @@ -235,11 +235,8 @@ private VersionStamp GetNewerVersion() return VersionStamp.Create(); } - public virtual async Task GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken) - { - var textAndVersion = await this.TextAndVersionSource.GetValueAsync(LoadTextOptions, cancellationToken).ConfigureAwait(false); - return textAndVersion.Version; - } + public virtual ValueTask GetTopLevelChangeTextVersionAsync(CancellationToken cancellationToken) + => this.TextAndVersionSource.GetVersionAsync(LoadTextOptions, cancellationToken); /// /// Only checks if the source of the text has changed, no content check is done. diff --git a/src/Workspaces/CoreTest/ChecksumTests.cs b/src/Workspaces/CoreTest/ChecksumTests.cs new file mode 100644 index 0000000000000..4b7570b35ec25 --- /dev/null +++ b/src/Workspaces/CoreTest/ChecksumTests.cs @@ -0,0 +1,61 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests +{ + public class ChecksumTests + { +#if NET + [Fact] + public void ValidateChecksumFromSpanSameAsChecksumFromBytes1() + { + var checksum1 = Checksum.Create("Goo"); + var checksum2 = Checksum.Create("Bar"); + + var checksumA = Checksum.TestAccessor.CreateUsingByteArrays(checksum1, checksum2); + var checksumB = Checksum.TestAccessor.CreateUsingSpans(checksum1, checksum2); + + Assert.Equal(checksumA, checksumB); + + Assert.NotEqual(checksum1, checksum2); + + Assert.NotEqual(checksum1, checksumA); + Assert.NotEqual(checksum1, checksumB); + Assert.NotEqual(checksum2, checksumA); + Assert.NotEqual(checksum2, checksumB); + } + + [Fact] + public void ValidateChecksumFromSpanSameAsChecksumFromBytes2() + { + var checksum1 = Checksum.Create("Goo"); + var checksum2 = Checksum.Create("Bar"); + var checksum3 = Checksum.Create("Baz"); + + var checksumA = Checksum.TestAccessor.CreateUsingByteArrays(checksum1, checksum2, checksum3); + var checksumB = Checksum.TestAccessor.CreateUsingSpans(checksum1, checksum2, checksum3); + + Assert.Equal(checksumA, checksumB); + + Assert.NotEqual(checksum1, checksum2); + Assert.NotEqual(checksum2, checksum3); + Assert.NotEqual(checksum3, checksum1); + + Assert.NotEqual(checksum1, checksumA); + Assert.NotEqual(checksum1, checksumB); + Assert.NotEqual(checksum2, checksumA); + Assert.NotEqual(checksum2, checksumB); + Assert.NotEqual(checksum3, checksumA); + Assert.NotEqual(checksum3, checksumB); + } +#endif + } +} diff --git a/src/Workspaces/CoreTest/CodeCleanup/AddMissingTokensTests.cs b/src/Workspaces/CoreTest/CodeCleanup/AddMissingTokensTests.cs index c7768bd5cbec2..ef761b40027fe 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/AddMissingTokensTests.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/AddMissingTokensTests.cs @@ -196,7 +196,7 @@ End Function } [Fact] - public async Task IdentifierMethod_DotName_DontAdd() + public async Task IdentifierMethod_DotName_DoNotAdd() { var code = @"Class A Function Test() As Integer @@ -560,7 +560,7 @@ public async Task MultipleLineIfStatementThenWithComment() } [Fact] - public async Task TypeArgumentOf_Comment_DontAdd() + public async Task TypeArgumentOf_Comment_DoNotAdd() { var code = @"[| Dim a As List( ' test @@ -729,7 +729,7 @@ End Sub } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544225")] - public async Task StructuredTrivia_Expression_DontCrash() + public async Task StructuredTrivia_Expression_DoNotCrash() { var code = @"[|#Const Goo1 = 1 #Const Goo2 = 2 @@ -933,7 +933,7 @@ End Sub } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544526")] - public async Task DontCrash_ImplementsStatement() + public async Task DoNotCrash_ImplementsStatement() { var code = @"[|Class C Sub Main() @@ -994,7 +994,7 @@ End Sub } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545256")] - public async Task HandlesClauseItem_DontAddParentheses() + public async Task HandlesClauseItem_DoNotAddParentheses() { var code = @"[|Structure s1 Sub Goo() Handles Me.Goo @@ -1011,7 +1011,7 @@ End Sub } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545380")] - public async Task DontAddParenthesesInForEachControlVariable() + public async Task DoNotAddParenthesesInForEachControlVariable() { var code = @"[|Module Module1 Sub Main() @@ -1034,7 +1034,7 @@ End Sub } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545380")] - public async Task DontAddParenthesesInForControlVariable() + public async Task DoNotAddParenthesesInForControlVariable() { var code = @"[|Module Module1 Sub Main() @@ -1057,7 +1057,7 @@ End Sub } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545483")] - public async Task DontAddParenthesesForMissingName() + public async Task DoNotAddParenthesesForMissingName() { var code = @"[|Class C Public Overrides Function|]"; diff --git a/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs b/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs index b2e9db7b48f27..97ebb13851341 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs @@ -268,7 +268,7 @@ public void MultipleRanges() => VerifyRange("namespace N { class C {|r:{ {|b:void Method() { }|} }|} class C2 {|r:{ {|b:void Method() { }|} }|} }"); [Fact, WorkItem(12848, "DevDiv_Projects/Roslyn")] - public void DontCrash_VB() + public void DoNotCrash_VB() { var code = @"#If DEBUG OrElse TRACE Then Imports System.Diagnostics @@ -288,7 +288,7 @@ Imports System.Diagnostics } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/774295")] - public async Task DontCrash_VB_2() + public async Task DoNotCrash_VB_2() { var code = @" Public Class Class1 diff --git a/src/Workspaces/CoreTest/CodeCleanup/NormalizeModifiersOrOperatorsTests.cs b/src/Workspaces/CoreTest/CodeCleanup/NormalizeModifiersOrOperatorsTests.cs index 7f2ce17ebd669..d96b7d100ec69 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/NormalizeModifiersOrOperatorsTests.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/NormalizeModifiersOrOperatorsTests.cs @@ -791,7 +791,7 @@ public async Task NormalizedOperator_StructuredTrivia() } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544520")] - public async Task DontRemoveByVal() + public async Task DoNotRemoveByVal() { var code = @"[|Module Program Sub Main( diff --git a/src/Workspaces/CoreTest/CodeCleanup/RemoveUnnecessaryLineContinuationTests.cs b/src/Workspaces/CoreTest/CodeCleanup/RemoveUnnecessaryLineContinuationTests.cs index 27f2257ec2516..17021a4a8a084 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/RemoveUnnecessaryLineContinuationTests.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/RemoveUnnecessaryLineContinuationTests.cs @@ -1132,7 +1132,7 @@ System.Diagnostics. _ [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530621")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/631933")] - public async Task DontRemoveLineContinuationAfterColonInSingleLineIfStatement() + public async Task DoNotRemoveLineContinuationAfterColonInSingleLineIfStatement() { var code = @"[|Module Program Dim x = Sub() If True Then Dim y : _ @@ -1149,7 +1149,7 @@ Exit Sub [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/609481")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/631933")] - public async Task DontRemoveLineContinuationInSingleLineIfStatement() + public async Task DoNotRemoveLineContinuationInSingleLineIfStatement() { var code = @"[| Module Program @@ -1179,7 +1179,7 @@ End Module [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/609481")] [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/631933")] - public async Task DontRemoveLineContinuationInNestedSingleLineIfStatement() + public async Task DoNotRemoveLineContinuationInNestedSingleLineIfStatement() { var code = @"[| Module Program @@ -1236,7 +1236,7 @@ End Module } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/710")] - public async Task DontRemoveLineContinuationInStringInterpolation1() + public async Task DoNotRemoveLineContinuationInStringInterpolation1() { var code = @"[| Module Program @@ -1255,7 +1255,7 @@ End Module } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/710")] - public async Task DontRemoveLineContinuationInStringInterpolation2() + public async Task DoNotRemoveLineContinuationInStringInterpolation2() { var code = @"[| Module Program @@ -1274,7 +1274,7 @@ End Module } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/710")] - public async Task DontRemoveLineContinuationInStringInterpolation3() + public async Task DoNotRemoveLineContinuationInStringInterpolation3() { var code = @"[| Module Program @@ -1299,7 +1299,7 @@ End Module } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1085887")] - public async Task DontRemoveLineContinuationInVisualBasic9() + public async Task DoNotRemoveLineContinuationInVisualBasic9() { var code = @"[| Module Program diff --git a/src/Workspaces/CoreTest/Options/OptionSerializerTests.cs b/src/Workspaces/CoreTest/Options/OptionSerializerTests.cs index 6c9b46356e93f..469db58299fea 100644 --- a/src/Workspaces/CoreTest/Options/OptionSerializerTests.cs +++ b/src/Workspaces/CoreTest/Options/OptionSerializerTests.cs @@ -6,7 +6,6 @@ using System.Linq; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; using Microsoft.CodeAnalysis.Editor.InlineDiagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Host; @@ -29,7 +28,6 @@ public void SerializationAndDeserializationForNullableBool([CombinatorialValues( CompletionViewOptionsStorage.EnableArgumentCompletionSnippets, FeatureOnOffOptions.OfferRemoveUnusedReferences, InheritanceMarginOptionsStorage.ShowInheritanceMargin, - SuggestionsOptionsStorage.Asynchronous, WorkspaceConfigurationOptionsStorage.EnableOpeningSourceGeneratedFilesInWorkspace, SolutionCrawlerOptionsStorage.EnableDiagnosticsInSourceGeneratedFiles, CompletionOptionsStorage.ShowItemsFromUnimportedNamespaces, diff --git a/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs b/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs index c61530bf283e3..0e811b5f95715 100644 --- a/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/AsyncLazyTests.cs @@ -23,7 +23,7 @@ public void GetValueAsyncReturnsCompletedTaskIfAsyncComputationCompletesImmediat // Note, this test may pass even if GetValueAsync posted a task to the threadpool, since the // current thread may context switch out and allow the threadpool to complete the task before // we check the state. However, a failure here definitely indicates a bug in AsyncLazy. - var lazy = new AsyncLazy(c => Task.FromResult(5), cacheResult: true); + var lazy = AsyncLazy.Create(c => Task.FromResult(5)); var t = lazy.GetValueAsync(CancellationToken.None); Assert.Equal(TaskStatus.RanToCompletion, t.Status); Assert.Equal(5, t.Result); @@ -68,8 +68,7 @@ private static void SynchronousContinuationsDoNotRunWithinGetValueCallCore(TaskS } return 42; - }, - cacheResult: false); + }); // Second, start a synchronous request. While we are in the GetValue, we will record which thread is being occupied by the request Thread synchronousRequestThread = null; @@ -174,7 +173,7 @@ private static void GetValueOrGetValueAsyncThrowsCorrectExceptionDuringCancellat { c.ThrowIfCancellationRequested(); } - }, synchronousComputeFunction: synchronousComputation, cacheResult: false); + }, synchronousComputeFunction: synchronousComputation); var cancellationTokenSource = new CancellationTokenSource(); @@ -201,14 +200,14 @@ public void GetValueAsyncThatIsCancelledReturnsTaskCancelledWithCorrectToken() { var cancellationTokenSource = new CancellationTokenSource(); - var lazy = new AsyncLazy(c => Task.Run((Func)(() => + var lazy = AsyncLazy.Create(c => Task.Run((Func)(() => { cancellationTokenSource.Cancel(); while (true) { c.ThrowIfCancellationRequested(); } - }), c), cacheResult: true); + }), c)); var task = lazy.GetValueAsync(cancellationTokenSource.Token); diff --git a/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj b/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj index 0eab42b807afb..bff1a9ca6a24a 100644 --- a/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj +++ b/src/Workspaces/CoreTestUtilities/Microsoft.CodeAnalysis.Workspaces.Test.Utilities.csproj @@ -38,12 +38,15 @@ + + + diff --git a/src/Workspaces/Remote/ServiceHub/Services/ClientOptionProviders.cs b/src/Workspaces/Remote/ServiceHub/Services/ClientOptionProviders.cs index a5684a319e667..5646371af297b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ClientOptionProviders.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ClientOptionProviders.cs @@ -34,7 +34,7 @@ public RemoteOptionsProviderCache(Func GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken) { - var lazyOptions = ImmutableInterlocked.GetOrAdd(ref _cache, languageServices.Language, _ => new AsyncLazy(GetRemoteOptions, cacheResult: true)); + var lazyOptions = ImmutableInterlocked.GetOrAdd(ref _cache, languageServices.Language, _ => AsyncLazy.Create(GetRemoteOptions)); return await lazyOptions.GetValueAsync(cancellationToken).ConfigureAwait(false); Task GetRemoteOptions(CancellationToken cancellationToken) diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs index a97a625c5fb4e..8422c9785de8e 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.Caching.cs @@ -58,7 +58,7 @@ public RemoteSemanticClassificationService(in ServiceConstructionArguments argum : base(arguments) { _workQueue = new AsyncBatchingWorkQueue<(Document, ClassificationType, ClassificationOptions)>( - DelayTimeSpan.Short, + DelayTimeSpan.NonFocus, CacheClassificationsAsync, EqualityComparer<(Document, ClassificationType, ClassificationOptions)>.Default, AsynchronousOperationListenerProvider.NullListener, @@ -132,7 +132,7 @@ private static string GetPersistenceName(ClassificationType type) if (matches) return; - using var _2 = ArrayBuilder.GetInstance(out var classifiedSpans); + using var _2 = Classifier.GetPooledList(out var classifiedSpans); // Compute classifications for the full span. var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -148,7 +148,7 @@ private static string GetPersistenceName(ClassificationType type) await storage.WriteStreamAsync(documentKey, persistenceName, stream, checksum, cancellationToken).ConfigureAwait(false); } - private static void WriteTo(ArrayBuilder classifiedSpans, ObjectWriter writer) + private static void WriteTo(SegmentedList classifiedSpans, ObjectWriter writer) { writer.WriteInt32(ClassificationFormat); diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index 6cf83d3d2b8ed..ee8f7ee46fe0e 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; @@ -40,7 +41,7 @@ protected override IRemoteSemanticClassificationService CreateService(in Service document = document.WithFrozenPartialSemantics(cancellationToken); } - using var _ = ArrayBuilder.GetInstance(out var temp); + using var _ = Classifier.GetPooledList(out var temp); await AbstractClassificationService.AddClassificationsInCurrentProcessAsync( document, span, type, options, temp, cancellationToken).ConfigureAwait(false); @@ -55,7 +56,7 @@ protected override IRemoteSemanticClassificationService CreateService(in Service _workQueue.AddWork((document, type, options)); } - return SerializableClassifiedSpans.Dehydrate(temp.ToImmutable()); + return SerializableClassifiedSpans.Dehydrate(temp.ToImmutableArray()); }, cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs index bff4e37ba80ff..20a6041c73759 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs @@ -4,13 +4,10 @@ using System; using System.Collections.Generic; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting @@ -215,8 +212,8 @@ public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions optio return AdjustSpacesOperationZeroOrOne(_options.Spacing.HasFlag(SpacePlacement.AfterComma)); } - // For "is [", "and [", but not "([" - if (previousKind != SyntaxKind.OpenParenToken) + // For "is [", "and [", but not "([" or "[[" + if (previousKind is not (SyntaxKind.OpenParenToken or SyntaxKind.OpenBracketToken)) { return CreateAdjustSpacesOperation(1, AdjustSpacesOption.ForceSpacesIfOnSingleLine); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index fee99dcd37ce8..caafc045b5bfa 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -1359,7 +1359,7 @@ public SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node) { var tupleExpression = (TupleExpressionSyntax)node; openParen = tupleExpression.OpenParenToken; - arguments = (SeparatedSyntaxList)tupleExpression.Arguments; + arguments = (SeparatedSyntaxList)(SeparatedSyntaxList)tupleExpression.Arguments; closeParen = tupleExpression.CloseParenToken; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 43ab916dddfda..b059ef0843c31 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -110,6 +110,7 @@ protected CSharpSyntaxKinds() public int? AndPattern => (int)SyntaxKind.AndPattern; public int? ConstantPattern => (int)SyntaxKind.ConstantPattern; public int? DeclarationPattern => (int)SyntaxKind.DeclarationPattern; + public int? ListPattern => (int)SyntaxKind.ListPattern; public int? NotPattern => (int)SyntaxKind.NotPattern; public int? OrPattern => (int)SyntaxKind.OrPattern; public int? ParenthesizedPattern => (int)SyntaxKind.ParenthesizedPattern; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs index 164dcc80267cf..fe73a70642643 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeActions/CodeActionRequestPriority.cs @@ -40,12 +40,15 @@ internal enum CodeActionRequestPriority Normal = 3, /// - /// Only high priority refactoring, code fix providers should be run. Specifically, - /// providers will be run when or - /// is . - /// The - /// will be run. + /// Only high priority refactoring, code fix providers should be run. Specifically, providers will be run when + /// or + /// is . The will be run. /// + /// Providers that return this should ensure that the appropriate s they return have a of + /// High = 4, } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CodeStyleHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CodeStyleHelpers.cs index 6bf8bdff681eb..7e8b4d4a98196 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CodeStyleHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CodeStyleHelpers.cs @@ -77,10 +77,9 @@ public static bool TryParseBoolEditorConfigCodeStyleOption(string arg, CodeStyle { // If we have two args, then the second must be a notification option. If // it isn't, then this isn't a valid code style option at all. - var secondValue = arg.Substring(firstColonIndex + 1); - if (TryParseNotification(secondValue, out var localNotification)) + if (TryParseNotification(arg.AsSpan(firstColonIndex + 1), out var localNotification)) { - var firstValue = arg.Substring(0, firstColonIndex); + var firstValue = arg[..firstColonIndex]; value = firstValue.Trim(); notification = localNotification; return true; @@ -93,7 +92,7 @@ public static bool TryParseBoolEditorConfigCodeStyleOption(string arg, CodeStyle return false; } - private static bool TryParseNotification(string value, out NotificationOption2 notification) + private static bool TryParseNotification(ReadOnlySpan value, out NotificationOption2 notification) { switch (value.Trim()) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs index eada350447e61..98d98c90e7b6c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticDescriptorExtensions.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.Shared.Extensions @@ -55,6 +56,44 @@ public static ImmutableArray ImmutableCustomTags(this DiagnosticDescript return effectiveSeverity; } + /// + /// Gets document-level effective severity of the given accounting for severity configurations from both the following sources: + /// 1. Compilation options from ruleset file, if any, and command line options such as /nowarn, /warnaserror, etc. + /// 2. Analyzer config documents at the document root directory or in ancestor directories. + /// + public static ReportDiagnostic GetEffectiveSeverity(this DiagnosticDescriptor descriptor, CompilationOptions compilationOptions, SyntaxTree tree, AnalyzerOptions analyzerOptions) + { + var effectiveSeverity = descriptor.GetEffectiveSeverity(compilationOptions); + + // Apply analyzer config options, unless configured with a non-default value in compilation options. + // Note that compilation options (/nowarn, /warnaserror) override analyzer config options. + if (!compilationOptions.SpecificDiagnosticOptions.TryGetValue(descriptor.Id, out var reportDiagnostic) || + reportDiagnostic == ReportDiagnostic.Default) + { + // First check for tree-level analyzer config options. + var analyzerConfigOptions = analyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(tree); + var providerAndTree = compilationOptions.SyntaxTreeOptionsProvider != null + ? (compilationOptions.SyntaxTreeOptionsProvider, tree) + : default; + var severityInEditorConfig = descriptor.GetEffectiveSeverity(analyzerConfigOptions, providerAndTree); + if (severityInEditorConfig != ReportDiagnostic.Default) + { + effectiveSeverity = severityInEditorConfig; + } + else + { + // If not found, check for global analyzer config options. + var severityInGlobalConfig = descriptor.GetEffectiveSeverity(analyzerOptions.AnalyzerConfigOptionsProvider.GlobalOptions, providerAndTree); + if (severityInGlobalConfig != ReportDiagnostic.Default) + { + effectiveSeverity = severityInGlobalConfig; + } + } + } + + return effectiveSeverity; + } + public static bool IsDefinedInEditorConfig(this DiagnosticDescriptor descriptor, AnalyzerConfigOptions analyzerConfigOptions) { // Check if the option is defined explicitly in the editorconfig @@ -97,14 +136,39 @@ public static bool IsDefinedInEditorConfig(this DiagnosticDescriptor descriptor, return false; } - public static ReportDiagnostic GetEffectiveSeverity(this DiagnosticDescriptor descriptor, AnalyzerConfigOptions analyzerConfigOptions) + /// + /// Gets the effective diagnostic severity for the diagnostic ID corresponding to the + /// given by looking up the severity settings in the options. + /// If the provided options are specific to a particular tree, provide a non-null value + /// for to look up tree specific severity options. + /// + public static ReportDiagnostic GetEffectiveSeverity( + this DiagnosticDescriptor descriptor, + AnalyzerConfigOptions analyzerConfigOptions, + (SyntaxTreeOptionsProvider provider, SyntaxTree tree)? providerAndTree = null) { + ReportDiagnostic severity; + string? value; + // Check if the option is defined explicitly in the editorconfig - var diagnosticKey = $"{DotnetDiagnosticPrefix}.{descriptor.Id}.{SeveritySuffix}"; - if (analyzerConfigOptions.TryGetValue(diagnosticKey, out var value) && - EditorConfigSeverityStrings.TryParse(value, out var severity)) + if (providerAndTree.HasValue) { - return severity; + var provider = providerAndTree.Value.provider; + var tree = providerAndTree.Value.tree; + if (provider.TryGetDiagnosticValue(tree, descriptor.Id, CancellationToken.None, out severity) || + provider.TryGetGlobalDiagnosticValue(descriptor.Id, CancellationToken.None, out severity)) + { + return severity; + } + } + else + { + var diagnosticKey = $"{DotnetDiagnosticPrefix}.{descriptor.Id}.{SeveritySuffix}"; + if (analyzerConfigOptions.TryGetValue(diagnosticKey, out value) && + EditorConfigSeverityStrings.TryParse(value, out severity)) + { + return severity; + } } // Check if the option is defined as part of a bulk configuration diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index b9695d23e8e1a..879b628a01346 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -78,5 +78,14 @@ public static int IndexOf(this IList list, Func predicate) return -1; } + + public static void AddRangeWhere(this List list, List collection, Func predicate) + { + foreach (var element in collection) + { + if (predicate(element)) + list.Add(element); + } + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs index 8b40d4b311747..a2247724a90de 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs @@ -42,6 +42,8 @@ public static ValueUsageInfo GetValueUsageInfo(this IOperation operation, ISymbo | out var x | | ✔️ | | | | ️ | case X x: | | ✔️ | | | | ️ | obj is X x | | ✔️ | | | | + | obj is { } x | | ✔️ | | | | + | obj is [] x | | ✔️ | | | | | ref var x = | | | ✔️ | ✔️ | | | ref readonly var x = | | | ✔️ | | | @@ -64,7 +66,7 @@ INegatedPatternOperation or switch (operation.Parent) { - case IPatternCaseClauseOperation _: + case IPatternCaseClauseOperation: // A declaration pattern within a pattern case clause is a // write for the declared local. // For example, 'x' is defined and assigned the value from 'obj' below: @@ -74,7 +76,7 @@ INegatedPatternOperation or // return ValueUsageInfo.Write; - case IRecursivePatternOperation _: + case IRecursivePatternOperation: // A declaration pattern within a recursive pattern is a // write for the declared local. // For example, 'x' is defined and assigned the value from 'obj' below: @@ -85,7 +87,7 @@ INegatedPatternOperation or // return ValueUsageInfo.Write; - case ISwitchExpressionArmOperation _: + case ISwitchExpressionArmOperation: // A declaration pattern within a switch expression arm is a // write for the declared local. // For example, 'x' is defined and assigned the value from 'obj' below: @@ -95,7 +97,7 @@ INegatedPatternOperation or // return ValueUsageInfo.Write; - case IIsPatternOperation _: + case IIsPatternOperation: // A declaration pattern within an is pattern is a // write for the declared local. // For example, 'x' is defined and assigned the value from 'obj' below: @@ -103,7 +105,7 @@ INegatedPatternOperation or // return ValueUsageInfo.Write; - case IPropertySubpatternOperation _: + case IPropertySubpatternOperation: // A declaration pattern within a property sub-pattern is a // write for the declared local. // For example, 'x' is defined and assigned the value from 'obj.Property' below: @@ -118,6 +120,10 @@ INegatedPatternOperation or return ValueUsageInfo.ReadWrite; } } + else if (operation is IRecursivePatternOperation or IListPatternOperation) + { + return ValueUsageInfo.Write; + } if (operation.Parent is IAssignmentOperation assignmentOperation && assignmentOperation.Target == operation) @@ -278,17 +284,17 @@ public static bool IsInLeftOfDeconstructionAssignment(this IOperation operation, } /// - /// Retursn true if the given operation is a regular compound assignment, + /// Returns true if the given operation is a regular compound assignment, /// i.e. such as a += b, - /// or a special null coalescing compoud assignment, i.e. + /// or a special null coalescing compound assignment, i.e. /// such as a ??= b. /// public static bool IsAnyCompoundAssignment(this IOperation operation) { switch (operation) { - case ICompoundAssignmentOperation _: - case ICoalesceAssignmentOperation _: + case ICompoundAssignmentOperation: + case ICoalesceAssignmentOperation: return true; default: diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs index af6ff0d545911..c0ad85b57b3b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs @@ -185,11 +185,13 @@ public void OnReadReferenceFound(ISymbol symbol) // Mark all the current reaching writes of symbol as read. if (SymbolsWriteBuilder.Count != 0) { - var currentWrites = CurrentBlockAnalysisData.GetCurrentWrites(symbol); - foreach (var write in currentWrites) - { - SymbolsWriteBuilder[(symbol, write)] = true; - } + CurrentBlockAnalysisData.ForEachCurrentWrite( + symbol, + static (write, arg) => + { + arg.self.SymbolsWriteBuilder[(arg.symbol, write)] = true; + }, + (symbol, self: this)); } // Mark the current symbol as read. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs index 1b2ad0a5726e5..45c98a5326b28 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs @@ -73,15 +73,30 @@ public void Clear(ISymbol symbol) /// /// Gets the currently reachable writes for the given symbol. /// - public IEnumerable GetCurrentWrites(ISymbol symbol) + public void ForEachCurrentWrite(ISymbol symbol, Action action, TArg arg) + { + ForEachCurrentWrite( + symbol, + static (write, arg) => + { + arg.action(write, arg.arg); + return true; + }, + (action, arg)); + } + + public bool ForEachCurrentWrite(ISymbol symbol, Func action, TArg arg) { if (_reachingWrites.TryGetValue(symbol, out var values)) { foreach (var value in values) { - yield return value; + if (!action(value, arg)) + return false; } } + + return true; } /// @@ -112,35 +127,49 @@ public bool Equals(BasicBlockAnalysisData other) return false; } - var uniqueSymbols = PooledHashSet.GetInstance(); - try + // Check if both _reachingWrites maps have same set of keys. This is a quick out based on O(keys), + // instead of doing the full O(k*v) check below. + foreach (var key in _reachingWrites.Keys) { - // Check if both _reachingWrites maps have same set of keys. - uniqueSymbols.AddRange(_reachingWrites.Keys); - uniqueSymbols.AddRange(other._reachingWrites.Keys); - if (uniqueSymbols.Count != _reachingWrites.Count) - { + if (!other._reachingWrites.ContainsKey(key)) return false; - } - - // Check if both _reachingWrites maps have same set of write - // operations for each tracked symbol. - foreach (var symbol in uniqueSymbols) - { - var writes1 = _reachingWrites[symbol]; - var writes2 = other._reachingWrites[symbol]; - if (!writes1.SetEquals(writes2)) - { - return false; - } - } + } - return true; + // Check if both _reachingWrites maps have same set of write operations for each tracked symbol. + foreach (var (symbol, writes1) in _reachingWrites) + { + var writes2 = other._reachingWrites[symbol]; + if (!SetEquals(writes1, writes2)) + return false; } - finally + + return true; + } + + /// + /// Same as , except this avoids allocations by + /// enumerating the set directly with a no-alloc enumerator. + /// + private static bool SetEquals(HashSet set1, HashSet set2) + { +#if NET8_0_OR_GREATER + // 📝 PERF: The boxed enumerator allocation that appears in some traces was fixed in .NET 8: + // https://github.com/dotnet/runtime/pull/78613 + return set1.SetEquals(set2); +#else + // same logic as https://github.com/dotnet/runtime/blob/62d6a8fe599ea3a77ef7af3c7660d398d692f062/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs#L1192 + + if (set1.Count != set2.Count) + return false; + + foreach (var operation in set1) { - uniqueSymbols.Free(); + if (!set2.Contains(operation)) + return false; } + + return true; +#endif } private bool IsEmpty => _reachingWrites.Count == 0; @@ -174,6 +203,7 @@ public bool Equals(BasicBlockAnalysisData other) } var mergedData = GetInstance(); + AddEntries(mergedData._reachingWrites, data1); AddEntries(mergedData._reachingWrites, data2); @@ -204,6 +234,9 @@ private static void AddEntries(Dictionary> re result.Add(symbol, values); } +#if NET + values.EnsureCapacity(values.Count + operations.Count); +#endif values.AddRange(operations); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs index 9acddc816678a..fa713fa175768 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs @@ -440,14 +440,16 @@ public void SetAnalysisDataOnMethodExit() { if (parameter.RefKind is RefKind.Ref or RefKind.Out) { - var currentWrites = CurrentBlockAnalysisData.GetCurrentWrites(parameter); - foreach (var write in currentWrites) - { - if (write != null) + CurrentBlockAnalysisData.ForEachCurrentWrite( + parameter, + static (write, arg) => { - SymbolsWriteBuilder[(parameter, write)] = true; - } - } + if (write != null) + { + arg.self.SymbolsWriteBuilder[(arg.parameter, write)] = true; + } + }, + (parameter, self: this)); } } } @@ -519,28 +521,40 @@ public override void SetTargetsFromSymbolForDelegate(IOperation write, ISymbol s // Action y = x; // var targetsBuilder = PooledHashSet.GetInstance(); - foreach (var symbolWrite in CurrentBlockAnalysisData.GetCurrentWrites(symbol)) - { - if (symbolWrite == null) + var completedVisit = CurrentBlockAnalysisData.ForEachCurrentWrite( + symbol, + static (symbolWrite, arg) => { - continue; - } + if (symbolWrite == null) + { + // Continue with the iteration + return true; + } - if (!_reachingDelegateCreationTargets.TryGetValue(symbolWrite, out var targetsBuilderForSymbolWrite)) - { - // Unable to find delegate creation targets for this symbol write. - // Bail out without setting targets. - targetsBuilder.Free(); - return; - } - else - { - foreach (var target in targetsBuilderForSymbolWrite) + if (!arg.self._reachingDelegateCreationTargets.TryGetValue(symbolWrite, out var targetsBuilderForSymbolWrite)) { - targetsBuilder.Add(target); + // Unable to find delegate creation targets for this symbol write. + // Bail out without setting targets. + arg.targetsBuilder.Free(); + + // Stop iterating here, even if early + return false; } - } - } + else + { + foreach (var target in targetsBuilderForSymbolWrite) + { + arg.targetsBuilder.Add(target); + } + } + + // Continue with the iteration + return true; + }, + (targetsBuilder, self: this)); + + if (!completedVisit) + return; _reachingDelegateCreationTargets[write] = targetsBuilder; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs index 1d59fb211c485..59dc0eaaca1ef 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs @@ -188,7 +188,7 @@ private void ProcessPendingWritesForAssignmentTarget(IAssignmentOperation operat { if (_pendingWritesMap.TryGetValue(operation, out var pendingWrites)) { - var isUsedCompountAssignment = operation.IsAnyCompoundAssignment() && + var isUsedCompoundAssignment = operation.IsAnyCompoundAssignment() && operation.Parent?.Kind != OperationKind.ExpressionStatement; foreach (var (symbolOpt, write) in pendingWrites) @@ -198,7 +198,7 @@ private void ProcessPendingWritesForAssignmentTarget(IAssignmentOperation operat Debug.Assert(symbolOpt != null); OnWriteReferenceFound(symbolOpt, write, ValueUsageInfo.Write); - if (isUsedCompountAssignment) + if (isUsedCompoundAssignment) { OnReadReferenceFound(symbolOpt); } @@ -283,7 +283,27 @@ public override void VisitFlowCaptureReference(IFlowCaptureReferenceOperation op public override void VisitDeclarationPattern(IDeclarationPatternOperation operation) { - if (operation.DeclaredSymbol != null) + if (operation.DeclaredSymbol is not null) + { + OnReferenceFound(operation.DeclaredSymbol, operation); + } + } + + public override void VisitRecursivePattern(IRecursivePatternOperation operation) + { + base.VisitRecursivePattern(operation); + + if (operation.DeclaredSymbol is not null) + { + OnReferenceFound(operation.DeclaredSymbol, operation); + } + } + + public override void VisitListPattern(IListPatternOperation operation) + { + base.VisitListPattern(operation); + + if (operation.DeclaredSymbol is not null) { OnReferenceFound(operation.DeclaredSymbol, operation); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 96256475649f9..056326d26eaa5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -4,12 +4,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -28,6 +29,11 @@ namespace Microsoft.CodeAnalysis.Formatting // that would create too big graph. key for this approach is how to reduce size of graph. internal abstract partial class AbstractFormatEngine { + // Intentionally do not trim the capacities of these collections down. We will repeatedly try to format large + // files as we edit them and this will produce a lot of garbage as we free the internal array backing the list + // over and over again. + private static readonly ObjectPool> s_tokenPairListPool = new(() => new(), trimOnFree: false); + private readonly ChainedFormattingRules _formattingRules; private readonly SyntaxNode _commonRoot; @@ -88,7 +94,8 @@ public AbstractFormattingResult Format(CancellationToken cancellationToken) var nodeOperations = CreateNodeOperations(cancellationToken); var tokenStream = new TokenStream(this.TreeData, Options, this.SpanToFormat, CreateTriviaFactory()); - var tokenOperation = CreateTokenOperation(tokenStream, cancellationToken); + using var tokenOperations = s_tokenPairListPool.GetPooledObject(); + AddTokenOperations(tokenStream, tokenOperations.Object, cancellationToken); // initialize context var context = CreateFormattingContext(tokenStream, cancellationToken); @@ -101,8 +108,7 @@ public AbstractFormattingResult Format(CancellationToken cancellationToken) ApplyBeginningOfTreeTriviaOperation(context, cancellationToken); - ApplyTokenOperations(context, nodeOperations, - tokenOperation, cancellationToken); + ApplyTokenOperations(context, nodeOperations, tokenOperations.Object, cancellationToken); ApplyTriviaOperations(context, cancellationToken); @@ -125,88 +131,56 @@ protected virtual NodeOperations CreateNodeOperations(CancellationToken cancella { cancellationToken.ThrowIfCancellationRequested(); - // iterating tree is very expensive. do it once and cache it to list - SegmentedList nodeIterator; - using (Logger.LogBlock(FunctionId.Formatting_IterateNodes, cancellationToken)) - { - const int magicLengthToNodesRatio = 5; - var result = new SegmentedList(Math.Max(this.SpanToFormat.Length / magicLengthToNodesRatio, 4)); + var nodeOperations = new NodeOperations(); - foreach (var node in _commonRoot.DescendantNodesAndSelf(this.SpanToFormat)) - { - cancellationToken.ThrowIfCancellationRequested(); - result.Add(node); - } + var indentBlockOperation = new List(); + var suppressOperation = new List(); + var alignmentOperation = new List(); + var anchorIndentationOperations = new List(); - nodeIterator = result; - } + // Cache delegates out here to avoid allocation overhead. - // iterate through each operation using index to not create any unnecessary object - cancellationToken.ThrowIfCancellationRequested(); - List indentBlockOperation; - using (Logger.LogBlock(FunctionId.Formatting_CollectIndentBlock, cancellationToken)) - { - indentBlockOperation = AddOperations(nodeIterator, _formattingRules.AddIndentBlockOperations, cancellationToken); - } + var addIndentBlockOperations = _formattingRules.AddIndentBlockOperations; + var addSuppressOperation = _formattingRules.AddSuppressOperations; + var addAlignTokensOperations = _formattingRules.AddAlignTokensOperations; + var addAnchorIndentationOperations = _formattingRules.AddAnchorIndentationOperations; - cancellationToken.ThrowIfCancellationRequested(); - List suppressOperation; - using (Logger.LogBlock(FunctionId.Formatting_CollectSuppressOperation, cancellationToken)) + // iterating tree is very expensive. only do it once. + foreach (var node in _commonRoot.DescendantNodesAndSelf(this.SpanToFormat)) { - suppressOperation = AddOperations(nodeIterator, _formattingRules.AddSuppressOperations, cancellationToken); - } - - cancellationToken.ThrowIfCancellationRequested(); - List alignmentOperation; - using (Logger.LogBlock(FunctionId.Formatting_CollectAlignOperation, cancellationToken)) - { - var operations = AddOperations(nodeIterator, _formattingRules.AddAlignTokensOperations, cancellationToken); - - // make sure we order align operation from left to right - operations.Sort((o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); + cancellationToken.ThrowIfCancellationRequested(); - alignmentOperation = operations; + AddOperations(nodeOperations.IndentBlockOperation, indentBlockOperation, node, addIndentBlockOperations); + AddOperations(nodeOperations.SuppressOperation, suppressOperation, node, addSuppressOperation); + AddOperations(nodeOperations.AlignmentOperation, alignmentOperation, node, addAlignTokensOperations); + AddOperations(nodeOperations.AnchorIndentationOperations, anchorIndentationOperations, node, addAnchorIndentationOperations); } - cancellationToken.ThrowIfCancellationRequested(); - List anchorIndentationOperations; - using (Logger.LogBlock(FunctionId.Formatting_CollectAnchorOperation, cancellationToken)) - { - anchorIndentationOperations = AddOperations(nodeIterator, _formattingRules.AddAnchorIndentationOperations, cancellationToken); - } + // make sure we order align operation from left to right + alignmentOperation.Sort(static (o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); - return new NodeOperations(indentBlockOperation, suppressOperation, anchorIndentationOperations, alignmentOperation); + return nodeOperations; } - private static List AddOperations(SegmentedList nodes, Action, SyntaxNode> addOperations, CancellationToken cancellationToken) + private static void AddOperations(List operations, List scratch, SyntaxNode node, Action, SyntaxNode> addOperations) { - var operations = new List(); - var list = new List(); - - foreach (var n in nodes) - { - cancellationToken.ThrowIfCancellationRequested(); - addOperations(list, n); + Debug.Assert(scratch.Count == 0); - list.RemoveAll(item => item == null); - operations.AddRange(list); - list.Clear(); - } + addOperations(scratch, node); - return operations; + operations.AddRangeWhere(scratch, static item => item is not null); + scratch.Clear(); } - private SegmentedArray CreateTokenOperation( + private void AddTokenOperations( TokenStream tokenStream, + SegmentedList list, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (Logger.LogBlock(FunctionId.Formatting_CollectTokenOperation, cancellationToken)) { - // pre-allocate list once. this is cheaper than re-adjusting list as items are added. - var list = new SegmentedArray(tokenStream.TokenCount - 1); - foreach (var (index, currentToken, nextToken) in tokenStream.TokenIterator) { cancellationToken.ThrowIfCancellationRequested(); @@ -214,17 +188,15 @@ private static List AddOperations(SegmentedList nodes, Action< var spaceOperation = _formattingRules.GetAdjustSpacesOperation(currentToken, nextToken); var lineOperation = _formattingRules.GetAdjustNewLinesOperation(currentToken, nextToken); - list[index] = new TokenPairWithOperations(tokenStream, index, spaceOperation, lineOperation); + list.Add(new TokenPairWithOperations(tokenStream, index, spaceOperation, lineOperation)); } - - return list; } } private void ApplyTokenOperations( FormattingContext context, NodeOperations nodeOperations, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, CancellationToken cancellationToken) { var applier = new OperationApplier(context, _formattingRules); @@ -326,7 +298,7 @@ private TextSpan GetSpanToFormat() FormattingContext context, NodeOperations nodeOperationsCollector, OperationApplier applier, CancellationToken cancellationToken) { // apply alignment operation - using (Logger.LogBlock(FunctionId.Formatting_CollectAlignOperation, cancellationToken)) + using (Logger.LogBlock(FunctionId.Formatting_ApplyAlignOperation, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); @@ -352,7 +324,7 @@ private TextSpan GetSpanToFormat() private static void ApplyAnchorOperations( FormattingContext context, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, OperationApplier applier, CancellationToken cancellationToken) { @@ -364,9 +336,7 @@ private TextSpan GetSpanToFormat() { cancellationToken.ThrowIfCancellationRequested(); if (!AnchorOperationCandidate(p)) - { continue; - } var pairIndex = p.PairIndex; applier.ApplyAnchorIndentation(pairIndex, previousChangesMap, cancellationToken); @@ -409,7 +379,7 @@ private static SyntaxToken FindCorrectBaseTokenOfRelativeIndentBlockOperation(In private static void ApplySpaceAndWrappingOperations( FormattingContext context, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, OperationApplier applier, CancellationToken cancellationToken) { @@ -417,9 +387,7 @@ private static SyntaxToken FindCorrectBaseTokenOfRelativeIndentBlockOperation(In { // go through each token pairs and apply operations foreach (var operationPair in tokenOperations) - { ApplySpaceAndWrappingOperationsBody(context, operationPair, applier, cancellationToken); - } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs index 7a3fbbaa4328c..d76367193f62d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs @@ -14,25 +14,9 @@ internal class NodeOperations { public static NodeOperations Empty = new(); - public List IndentBlockOperation { get; } - public List SuppressOperation { get; } - public List AlignmentOperation { get; } - public List AnchorIndentationOperations { get; } - - public NodeOperations(List indentBlockOperation, List suppressOperation, List anchorIndentationOperations, List alignmentOperation) - { - this.IndentBlockOperation = indentBlockOperation; - this.SuppressOperation = suppressOperation; - this.AlignmentOperation = alignmentOperation; - this.AnchorIndentationOperations = anchorIndentationOperations; - } - - private NodeOperations() - { - this.IndentBlockOperation = new List(); - this.SuppressOperation = new List(); - this.AlignmentOperation = new List(); - this.AnchorIndentationOperations = new List(); - } + public List IndentBlockOperation { get; } = new(); + public List SuppressOperation { get; } = new(); + public List AlignmentOperation { get; } = new(); + public List AnchorIndentationOperations { get; } = new(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs index 79efdb4985940..fd8fc491ce50a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.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; -using System.Collections.Generic; using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Formatting @@ -12,20 +10,17 @@ internal partial class TokenStream { // gain of having hand written iterator seems about 50-100ms over auto generated one. // not sure whether it is worth it. but I already wrote it to test, so going to just keep it. - private class Iterator : IEnumerable<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> + public readonly struct Iterator { private readonly SegmentedList _tokensIncludingZeroWidth; public Iterator(SegmentedList tokensIncludingZeroWidth) => _tokensIncludingZeroWidth = tokensIncludingZeroWidth; - public IEnumerator<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> GetEnumerator() - => new Enumerator(_tokensIncludingZeroWidth); + public Enumerator GetEnumerator() + => new(_tokensIncludingZeroWidth); - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - => GetEnumerator(); - - private struct Enumerator : IEnumerator<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> + public struct Enumerator { private readonly SegmentedList _tokensIncludingZeroWidth; private readonly int _maxCount; @@ -42,10 +37,6 @@ public Enumerator(SegmentedList tokensIncludingZeroWidth) _current = default; } - public readonly void Dispose() - { - } - public bool MoveNext() { if (_index < _maxCount) @@ -66,25 +57,6 @@ private bool MoveNextRare() } public readonly (int index, SyntaxToken currentToken, SyntaxToken nextToken) Current => _current; - - readonly object System.Collections.IEnumerator.Current - { - get - { - if (_index == 0 || _index == _maxCount + 1) - { - throw new InvalidOperationException(); - } - - return Current; - } - } - - void System.Collections.IEnumerator.Reset() - { - _index = 0; - _current = new ValueTuple(); - } } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs index adb3cef214c0c..f1062caa03050 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs @@ -550,13 +550,8 @@ private int GetTokenIndexInStream(SyntaxToken token) return -1; } - public IEnumerable<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> TokenIterator - { - get - { - return new Iterator(_tokens); - } - } + public Iterator TokenIterator + => new(_tokens); private sealed class TokenOrderComparer : IComparer { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index c0d57e6ac0ee4..fb265dad84e82 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -111,11 +111,11 @@ internal enum FunctionId Formatting_ContextInitialization = 85, Formatting_Format = 86, Formatting_ApplyResultToBuffer = 87, - Formatting_IterateNodes = 88, - Formatting_CollectIndentBlock = 89, - Formatting_CollectSuppressOperation = 90, - Formatting_CollectAlignOperation = 91, - Formatting_CollectAnchorOperation = 92, + // obsolete: Formatting_IterateNodes = 88, + // obsolete: Formatting_CollectIndentBlock = 89, + // obsolete: Formatting_CollectSuppressOperation = 90, + // obsolete: Formatting_CollectAlignOperation = 91, + // obsolete: Formatting_CollectAnchorOperation = 92, Formatting_CollectTokenOperation = 93, Formatting_BuildContext = 94, Formatting_ApplySpaceAndLine = 95, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs index 9088a00a57d7b..f41c902d9fbc5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -26,13 +27,15 @@ public static PooledObject> GetPooledObject(this ObjectPool< public static PooledObject> GetPooledObject(this ObjectPool> pool) => PooledObject>.Create(pool); - public static PooledObject> GetPooledObject(this ObjectPool> pool) - where TKey : notnull + public static PooledObject> GetPooledObject(this ObjectPool> pool) where TKey : notnull => PooledObject>.Create(pool); public static PooledObject> GetPooledObject(this ObjectPool> pool) => PooledObject>.Create(pool); + public static PooledObject> GetPooledObject(this ObjectPool> pool) + => PooledObject>.Create(pool); + public static PooledObject> GetPooledObject(this ObjectPool> pool, out List list) { var pooledObject = PooledObject>.Create(pool); @@ -75,6 +78,14 @@ public static HashSet AllocateAndClear(this ObjectPool> pool) return set; } + public static SegmentedHashSet AllocateAndClear(this ObjectPool> pool) + { + var set = pool.Allocate(); + set.Clear(); + + return set; + } + public static Dictionary AllocateAndClear(this ObjectPool> pool) where TKey : notnull { @@ -92,6 +103,14 @@ public static List AllocateAndClear(this ObjectPool> pool) return list; } + public static SegmentedList AllocateAndClear(this ObjectPool> pool) + { + var list = pool.Allocate(); + list.Clear(); + + return list; + } + public static void ClearAndFree(this ObjectPool pool, StringBuilder sb) { if (sb == null) @@ -127,6 +146,24 @@ public static void ClearAndFree(this ObjectPool> pool, HashSet pool.Free(set); } + public static void ClearAndFree(this ObjectPool> pool, SegmentedHashSet set) + { + if (set == null) + { + return; + } + + var count = set.Count; + set.Clear(); + + if (count > Threshold) + { + set.TrimExcess(); + } + + pool.Free(set); + } + public static void ClearAndFree(this ObjectPool> pool, ConcurrentSet set) where T : notnull { if (set == null) @@ -231,7 +268,24 @@ public static void ClearAndFree(this ObjectPool> pool, Queue set) pool.Free(map); } - public static void ClearAndFree(this ObjectPool> pool, List list) + public static void ClearAndFree(this ObjectPool> pool, List list, bool trim = true) + { + if (list == null) + { + return; + } + + list.Clear(); + + if (trim && list.Capacity > Threshold) + { + list.Capacity = Threshold; + } + + pool.Free(list); + } + + public static void ClearAndFree(this ObjectPool> pool, SegmentedList list, bool trim = true) { if (list == null) { @@ -240,7 +294,7 @@ public static void ClearAndFree(this ObjectPool> pool, List list) list.Clear(); - if (list.Capacity > Threshold) + if (trim && list.Capacity > Threshold) { list.Capacity = Threshold; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs index 7e9abd53c845e..9078f5f0fd50c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Text; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis @@ -41,32 +42,32 @@ public static PooledObject Create(ObjectPool pool) { return new PooledObject( pool, - Allocator, - Releaser); + static p => Allocator(p), + static (p, v) => Releaser(p, v)); } public static PooledObject> Create(ObjectPool> pool) { return new PooledObject>( pool, - p => Allocator(p), - (p, sb) => Releaser(p, sb)); + static p => Allocator(p), + static (p, v) => Releaser(p, v)); } public static PooledObject> Create(ObjectPool> pool) { return new PooledObject>( pool, - p => Allocator(p), - (p, sb) => Releaser(p, sb)); + static p => Allocator(p), + static (p, v) => Releaser(p, v)); } public static PooledObject> Create(ObjectPool> pool) { return new PooledObject>( pool, - p => Allocator(p), - (p, sb) => Releaser(p, sb)); + static p => Allocator(p), + static (p, v) => Releaser(p, v)); } public static PooledObject> Create(ObjectPool> pool) @@ -74,17 +75,26 @@ public static PooledObject> Create(ObjectPool>( pool, - p => Allocator(p), - (p, sb) => Releaser(p, sb)); + static p => Allocator(p), + static (p, v) => Releaser(p, v)); } public static PooledObject> Create(ObjectPool> pool) { return new PooledObject>( pool, - p => Allocator(p), - (p, sb) => Releaser(p, sb)); + static p => Allocator(p), + static (p, v) => Releaser(p, v)); } + + public static PooledObject> Create(ObjectPool> pool) + { + return new PooledObject>( + pool, + static p => Allocator(p), + static (p, v) => Releaser(p, v)); + } + #endregion #region allocators and releasers @@ -124,7 +134,13 @@ private static List Allocator(ObjectPool> pool) => pool.AllocateAndClear(); private static void Releaser(ObjectPool> pool, List obj) - => pool.ClearAndFree(obj); + => pool.ClearAndFree(obj, pool.TrimOnFree); + + private static SegmentedList Allocator(ObjectPool> pool) + => pool.AllocateAndClear(); + + private static void Releaser(ObjectPool> pool, SegmentedList obj) + => pool.ClearAndFree(obj, pool.TrimOnFree); #endregion } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/EditorConfigValueSerializer`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/EditorConfigValueSerializer`1.cs index fe106475553d6..cfb2d1fc095ad 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/EditorConfigValueSerializer`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/EditorConfigValueSerializer`1.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeStyle; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options @@ -21,6 +21,8 @@ internal sealed class EditorConfigValueSerializer : IEditorConfigValueSeriali private readonly Func> _parseValue; private readonly Func _serializeValue; + private readonly ConcurrentDictionary> _cachedValues = new(); + public EditorConfigValueSerializer( Func> parseValue, Func serializeValue) @@ -43,7 +45,7 @@ bool IEditorConfigValueSerializer.TryParse(string value, out object? result) internal bool TryParseValue(string value, [MaybeNullWhen(false)] out T result) { - var optionalValue = _parseValue(value); + var optionalValue = _cachedValues.GetOrAdd(value, _parseValue); if (optionalValue.HasValue) { result = optionalValue.Value; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index d9eae6e1796a5..71017634ed37c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -861,6 +861,9 @@ public static bool IsConstantPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen public static bool IsDeclarationPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.DeclarationPattern; + public static bool IsListPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.ListPattern; + public static bool IsNotPattern(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.NotPattern; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index 9821e1fe36b54..d3ecef35fa2a7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -158,6 +158,7 @@ internal interface ISyntaxKinds int? AndPattern { get; } int? ConstantPattern { get; } int? DeclarationPattern { get; } + int? ListPattern { get; } int? NotPattern { get; } int? OrPattern { get; } int? ParenthesizedPattern { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs index beb42443acbcf..73371861e1f11 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AsyncLazy`1.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -13,8 +14,8 @@ namespace Roslyn.Utilities { internal static class AsyncLazy { - public static AsyncLazy Create(Func> asynchronousComputeFunction, bool cacheResult) - => new(asynchronousComputeFunction, cacheResult); + public static AsyncLazy Create(Func> asynchronousComputeFunction) + => new(asynchronousComputeFunction); public static AsyncLazy Create(T value) => new(value); @@ -47,11 +48,6 @@ internal sealed class AsyncLazy : ValueSource /// private Func? _synchronousComputeFunction; - /// - /// Whether or not we should keep the value around once we've computed it. - /// - private readonly bool _cacheResult; - /// /// The Task that holds the cached result. /// @@ -88,12 +84,20 @@ internal sealed class AsyncLazy : ValueSource /// public AsyncLazy(T value) { - _cacheResult = true; _cachedResult = Task.FromResult(value); } +#pragma warning disable IDE0060 // Remove unused parameter + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("'cacheResult' is no longer supported. Use constructor without it.", error: false)] public AsyncLazy(Func> asynchronousComputeFunction, bool cacheResult) - : this(asynchronousComputeFunction, synchronousComputeFunction: null, cacheResult: cacheResult) + : this(asynchronousComputeFunction) + { + } +#pragma warning restore IDE0060 // Remove unused parameter + + public AsyncLazy(Func> asynchronousComputeFunction) + : this(asynchronousComputeFunction, synchronousComputeFunction: null) { } @@ -107,14 +111,11 @@ public AsyncLazy(Func> asynchronousComputeFunction, b /// is allowed to block. This function should not be implemented by a simple Wait on the /// asynchronous value. If that's all you are doing, just don't pass a synchronous function /// in the first place. - /// Whether the result should be cached once the computation is - /// complete. - public AsyncLazy(Func> asynchronousComputeFunction, Func? synchronousComputeFunction, bool cacheResult) + public AsyncLazy(Func> asynchronousComputeFunction, Func? synchronousComputeFunction) { Contract.ThrowIfNull(asynchronousComputeFunction); _asynchronousComputeFunction = asynchronousComputeFunction; _synchronousComputeFunction = synchronousComputeFunction; - _cacheResult = cacheResult; } #region Lock Wrapper for Invariant Checking @@ -472,22 +473,18 @@ private void CompleteWithTask(Task task, CancellationToken cancellationToken) private Task GetCachedValueAndCacheThisValueIfNoneCached_NoLock(Task task) { if (_cachedResult != null) - { return _cachedResult; - } - else - { - if (_cacheResult && task.Status == TaskStatus.RanToCompletion) - { - // Hold onto the completed task. We can get rid of the computation functions for good - _cachedResult = task; - _asynchronousComputeFunction = null; - _synchronousComputeFunction = null; - } + if (task.Status == TaskStatus.RanToCompletion) + { + // Hold onto the completed task. We can get rid of the computation functions for good + _cachedResult = task; - return task; + _asynchronousComputeFunction = null; + _synchronousComputeFunction = null; } + + return task; } private void OnAsynchronousRequestCancelled(object? state) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ICollectionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ICollectionExtensions.cs index 84acb5b771676..9a144be726646 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ICollectionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ICollectionExtensions.cs @@ -26,6 +26,30 @@ public static void AddRange(this ICollection collection, IEnumerable? v } } + public static void AddRange(this ICollection collection, HashSet? values) + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + if (values != null) + { + foreach (var item in values) + collection.Add(item); + } + } + + public static void AddRange(this ICollection collection, Dictionary.KeyCollection? keyCollection) where TKey : notnull + { + if (collection == null) + throw new ArgumentNullException(nameof(collection)); + + if (keyCollection != null) + { + foreach (var key in keyCollection) + collection.Add(key); + } + } + public static void AddRange(this ICollection collection, ImmutableArray values) { if (collection == null) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 8f52c36c22324..c5684f088a58a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -113,6 +113,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property AndPattern As Integer? Implements ISyntaxKinds.AndPattern Public ReadOnly Property ConstantPattern As Integer? Implements ISyntaxKinds.ConstantPattern Public ReadOnly Property DeclarationPattern As Integer? Implements ISyntaxKinds.DeclarationPattern + Public ReadOnly Property ListPattern As Integer? Implements ISyntaxKinds.ListPattern Public ReadOnly Property NotPattern As Integer? Implements ISyntaxKinds.NotPattern Public ReadOnly Property OrPattern As Integer? Implements ISyntaxKinds.OrPattern Public ReadOnly Property ParenthesizedPattern As Integer? Implements ISyntaxKinds.ParenthesizedPattern diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs index 246aabc49f2b5..8304be44689af 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/CSharpSyntaxContext.cs @@ -97,6 +97,7 @@ internal sealed class CSharpSyntaxContext : SyntaxContext bool isPreProcessorExpressionContext, bool isPreProcessorKeywordContext, bool isPrimaryFunctionExpressionContext, + bool isRightAfterUsingOrImportDirective, bool isRightOfNameSeparator, bool isRightSideOfNumericType, bool isStatementContext, @@ -132,6 +133,7 @@ internal sealed class CSharpSyntaxContext : SyntaxContext isPossibleTupleContext: isPossibleTupleContext, isPreProcessorDirectiveContext: isPreProcessorDirectiveContext, isPreProcessorExpressionContext: isPreProcessorExpressionContext, + isRightAfterUsingOrImportDirective: isRightAfterUsingOrImportDirective, isRightOfNameSeparator: isRightOfNameSeparator, isRightSideOfNumericType: isRightSideOfNumericType, isStatementContext: isStatementContext, @@ -291,6 +293,7 @@ public static CSharpSyntaxContext CreateContext(Document document, SemanticModel isPreProcessorExpressionContext: isPreProcessorExpressionContext, isPreProcessorKeywordContext: isPreProcessorKeywordContext, isPrimaryFunctionExpressionContext: syntaxTree.IsPrimaryFunctionExpressionContext(position, leftToken), + isRightAfterUsingOrImportDirective: targetToken.Parent is UsingDirectiveSyntax usingDirective && usingDirective?.GetLastToken() == targetToken, isRightOfNameSeparator: syntaxTree.IsRightOfDotOrArrowOrColonColon(position, targetToken, cancellationToken), isRightSideOfNumericType: isRightSideOfNumericType, isStatementContext: isStatementContext, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/SyntaxContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/SyntaxContext.cs index eca491f3345db..edee23dce3cce 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/SyntaxContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/SyntaxContext.cs @@ -46,6 +46,7 @@ internal abstract class SyntaxContext public bool IsPossibleTupleContext { get; } public bool IsPreProcessorDirectiveContext { get; } public bool IsPreProcessorExpressionContext { get; } + public bool IsRightAfterUsingOrImportDirective { get; } public bool IsRightOfNameSeparator { get; } public bool IsRightSideOfNumericType { get; } public bool IsStatementContext { get; } @@ -79,6 +80,7 @@ internal abstract class SyntaxContext bool isPossibleTupleContext, bool isPreProcessorDirectiveContext, bool isPreProcessorExpressionContext, + bool isRightAfterUsingOrImportDirective, bool isRightOfNameSeparator, bool isRightSideOfNumericType, bool isStatementContext, @@ -112,6 +114,7 @@ internal abstract class SyntaxContext this.IsPossibleTupleContext = isPossibleTupleContext; this.IsPreProcessorDirectiveContext = isPreProcessorDirectiveContext; this.IsPreProcessorExpressionContext = isPreProcessorExpressionContext; + this.IsRightAfterUsingOrImportDirective = isRightAfterUsingOrImportDirective; this.IsRightOfNameSeparator = isRightOfNameSeparator; this.IsRightSideOfNumericType = isRightSideOfNumericType; this.IsStatementContext = isStatementContext; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs index cb82cbcec55fa..8bd08c37799f6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Simplification { internal static class SimplificationHelpers { - public static readonly SyntaxAnnotation DontSimplifyAnnotation = new(); + public static readonly SyntaxAnnotation DoNotSimplifyAnnotation = new(); public static readonly SyntaxAnnotation SimplifyModuleNameAnnotation = new(); public static TNode CopyAnnotations(SyntaxNode from, TNode to) where TNode : SyntaxNode @@ -32,7 +32,7 @@ internal static class SimplificationHelpers if (dontSimplifyResult) { - to = to.WithAdditionalAnnotations(DontSimplifyAnnotation); + to = to.WithAdditionalAnnotations(DoNotSimplifyAnnotation); } return to; @@ -55,7 +55,7 @@ public static SyntaxToken CopyAnnotations(SyntaxToken from, SyntaxToken to) if (dontSimplifyResult) { - to = to.WithAdditionalAnnotations(DontSimplifyAnnotation); + to = to.WithAdditionalAnnotations(DoNotSimplifyAnnotation); } return to; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb index 4df2b5f12b6c1..15a4e8dff9be4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Extensions/ContextQuery/VisualBasicSyntaxContext.vb @@ -73,6 +73,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery isPossibleTupleContext As Boolean, isPreProcessorDirectiveContext As Boolean, isPreProcessorExpressionContext As Boolean, + isRightAfterUsingOrImportDirective As Boolean, isRightOfNameSeparator As Boolean, isRightSideOfNumericType As Boolean, isStatementContext As Boolean, @@ -105,6 +106,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery isPossibleTupleContext:=isPossibleTupleContext, isPreProcessorDirectiveContext:=isPreProcessorDirectiveContext, isPreProcessorExpressionContext:=isPreProcessorExpressionContext, + isRightAfterUsingOrImportDirective:=isRightAfterUsingOrImportDirective, isRightOfNameSeparator:=isRightOfNameSeparator, isRightSideOfNumericType:=isRightSideOfNumericType, isStatementContext:=isStatementContext, @@ -187,6 +189,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery isPossibleTupleContext:=syntaxTree.IsPossibleTupleContext(targetToken, position), isPreProcessorDirectiveContext:=syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken), isPreProcessorExpressionContext:=syntaxTree.IsInPreprocessorExpressionContext(position, cancellationToken), + isRightAfterUsingOrImportDirective:=ComputeIsRightAfterUsingOrImportDirective(targetToken), isRightOfNameSeparator:=syntaxTree.IsRightOfDot(position, cancellationToken), isRightSideOfNumericType:=False, isStatementContext:=isStatementContext, @@ -285,6 +288,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery targetToken = enumDeclaration.UnderlyingType.AsKeyword End Function + Private Shared Function ComputeIsRightAfterUsingOrImportDirective(targetToken As SyntaxToken) As Boolean + Dim importStatement = targetToken.GetAncestor(Function(n) n.IsKind(SyntaxKind.ImportsStatement)) + Dim lastToken = importStatement?.GetLastToken() + Return lastToken.HasValue AndAlso lastToken.Value = targetToken + End Function + Public Function IsFollowingParameterListOrAsClauseOfMethodDeclaration() As Boolean If TargetToken.FollowsEndOfStatement(Position) Then Return False diff --git a/src/Workspaces/VisualBasic/Portable/Classification/ClassificationHelpers.vb b/src/Workspaces/VisualBasic/Portable/Classification/ClassificationHelpers.vb index 03d3f584d4eec..72056bbc5e836 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/ClassificationHelpers.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/ClassificationHelpers.vb @@ -4,6 +4,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Classification +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -318,7 +319,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification End Select End Function - Friend Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Friend Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim text2 = text.ToString(textSpan) Dim tokens = SyntaxFactory.ParseTokens(text2, initialTokenPosition:=textSpan.Start) Worker.CollectClassifiedSpans(tokens, textSpan, result, cancellationToken) diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/IdentifierNameSyntaxClassifier.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/IdentifierNameSyntaxClassifier.vb index caa1740925821..31a2cfcc2be14 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/IdentifierNameSyntaxClassifier.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/IdentifierNameSyntaxClassifier.vb @@ -6,7 +6,7 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Classification Imports Microsoft.CodeAnalysis.Classification.Classifiers -Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers @@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers Public Overrides ReadOnly Property SyntaxNodeTypes As ImmutableArray(Of Type) = ImmutableArray.Create(GetType(IdentifierNameSyntax)) - Public Overrides Sub AddClassifications(syntax As SyntaxNode, semanticModel As SemanticModel, options As ClassificationOptions, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Public Overrides Sub AddClassifications(syntax As SyntaxNode, semanticModel As SemanticModel, options As ClassificationOptions, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim identifierName = DirectCast(syntax, IdentifierNameSyntax) Dim identifier = identifierName.Identifier If CaseInsensitiveComparison.Equals(identifier.ValueText, s_awaitText) Then diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/ImportAliasClauseSyntaxClassifier.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/ImportAliasClauseSyntaxClassifier.vb index dc4fa5ad64fae..088851e6032b2 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/ImportAliasClauseSyntaxClassifier.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/ImportAliasClauseSyntaxClassifier.vb @@ -6,6 +6,7 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Classification Imports Microsoft.CodeAnalysis.Classification.Classifiers +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -15,14 +16,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers Public Overrides ReadOnly Property SyntaxNodeTypes As ImmutableArray(Of Type) = ImmutableArray.Create(GetType(ImportAliasClauseSyntax)) - Public Overrides Sub AddClassifications(syntax As SyntaxNode, semanticModel As SemanticModel, options As ClassificationOptions, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Public Overrides Sub AddClassifications(syntax As SyntaxNode, semanticModel As SemanticModel, options As ClassificationOptions, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) ClassifyImportAliasClauseSyntax(DirectCast(syntax, ImportAliasClauseSyntax), semanticModel, result, cancellationToken) End Sub Private Shared Sub ClassifyImportAliasClauseSyntax( node As ImportAliasClauseSyntax, semanticModel As SemanticModel, - result As ArrayBuilder(Of ClassifiedSpan), + result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim symbolInfo = semanticModel.GetTypeInfo(DirectCast(node.Parent, SimpleImportsClauseSyntax).Name, cancellationToken) diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb index 2498ae9daa736..52b66d95b1e9f 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.vb @@ -6,6 +6,7 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Classification Imports Microsoft.CodeAnalysis.Classification.Classifiers +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -24,7 +25,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers syntax As SyntaxNode, semanticModel As SemanticModel, options As ClassificationOptions, - result As ArrayBuilder(Of ClassifiedSpan), + result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim nameSyntax = TryCast(syntax, NameSyntax) @@ -67,7 +68,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers Private Sub ClassifyNameSyntax( node As NameSyntax, semanticModel As SemanticModel, - result As ArrayBuilder(Of ClassifiedSpan), + result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim classifiedSpan As ClassifiedSpan @@ -234,7 +235,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers Private Shared Sub ClassifyModifiedIdentifier( modifiedIdentifier As ModifiedIdentifierSyntax, - result As ArrayBuilder(Of ClassifiedSpan)) + result As SegmentedList(Of ClassifiedSpan)) If modifiedIdentifier.ArrayBounds IsNot Nothing OrElse modifiedIdentifier.ArrayRankSpecifiers.Count > 0 OrElse @@ -274,7 +275,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers End Select End Function - Private Shared Sub ClassifyMethodStatement(methodStatement As MethodStatementSyntax, semanticModel As SemanticModel, result As ArrayBuilder(Of ClassifiedSpan)) + Private Shared Sub ClassifyMethodStatement(methodStatement As MethodStatementSyntax, semanticModel As SemanticModel, result As SegmentedList(Of ClassifiedSpan)) ' Ensure that extension method declarations are classified properly. ' Note that the method statement name is likely already classified as a method name ' by the syntactic classifier. However, there isn't away to determine whether a VB @@ -287,7 +288,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers Private Shared Sub ClassifyLabelSyntax( node As LabelSyntax, - result As ArrayBuilder(Of ClassifiedSpan)) + result As SegmentedList(Of ClassifiedSpan)) result.Add(New ClassifiedSpan(node.LabelToken.Span, ClassificationTypeNames.LabelName)) End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.vb index 3d7256b731125..df6355a9d29d3 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/OperatorOverloadSyntaxClassifier.vb @@ -6,7 +6,7 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Classification Imports Microsoft.CodeAnalysis.Classification.Classifiers -Imports Microsoft.CodeAnalysis.PooledObjects +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers @@ -21,8 +21,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification.Classifiers Public Overrides Sub AddClassifications( syntax As SyntaxNode, semanticModel As SemanticModel, -options As ClassificationOptions, - result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + options As ClassificationOptions, + result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim symbolInfo = semanticModel.GetSymbolInfo(syntax, cancellationToken) If TypeOf symbolInfo.Symbol Is IMethodSymbol AndAlso diff --git a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicSyntaxClassificationService.vb b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicSyntaxClassificationService.vb index b95d7e81412c3..f18a9d12b1188 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicSyntaxClassificationService.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/SyntaxClassification/VisualBasicSyntaxClassificationService.vb @@ -7,6 +7,7 @@ Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis.Classification Imports Microsoft.CodeAnalysis.Classification.Classifiers +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text @@ -32,11 +33,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification Return s_defaultSyntaxClassifiers End Function - Public Overrides Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Public Overrides Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) ClassificationHelpers.AddLexicalClassifications(text, textSpan, result, cancellationToken) End Sub - Public Overrides Sub AddSyntacticClassifications(root As SyntaxNode, textSpan As TextSpan, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Public Overrides Sub AddSyntacticClassifications(root As SyntaxNode, textSpan As TextSpan, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Worker.CollectClassifiedSpans(root, textSpan, result, cancellationToken) End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Classification/VisualBasicClassificationService.vb b/src/Workspaces/VisualBasic/Portable/Classification/VisualBasicClassificationService.vb index 94ed43b998979..fa6dc0e3b2f12 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/VisualBasicClassificationService.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/VisualBasicClassificationService.vb @@ -5,8 +5,8 @@ Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis.Classification +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.Host.Mef -Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic.Classification @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification Public Sub New() End Sub - Public Overrides Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Public Overrides Sub AddLexicalClassifications(text As SourceText, textSpan As TextSpan, result As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) ClassificationHelpers.AddLexicalClassifications(text, textSpan, result, cancellationToken) End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Classification/Worker.vb b/src/Workspaces/VisualBasic/Portable/Classification/Worker.vb index c465d12d00b4b..cffae99c9adda 100644 --- a/src/Workspaces/VisualBasic/Portable/Classification/Worker.vb +++ b/src/Workspaces/VisualBasic/Portable/Classification/Worker.vb @@ -4,19 +4,20 @@ Imports System.Threading Imports Microsoft.CodeAnalysis.Classification +Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Classification Partial Friend Class Worker - Private ReadOnly _list As ArrayBuilder(Of ClassifiedSpan) + Private ReadOnly _list As SegmentedList(Of ClassifiedSpan) Private ReadOnly _textSpan As TextSpan Private ReadOnly _docCommentClassifier As DocumentationCommentClassifier Private ReadOnly _xmlClassifier As XmlClassifier Private ReadOnly _cancellationToken As CancellationToken - Private Sub New(textSpan As TextSpan, list As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + Private Sub New(textSpan As TextSpan, list As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) _textSpan = textSpan _list = list _docCommentClassifier = New DocumentationCommentClassifier(Me) @@ -25,7 +26,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification End Sub Friend Shared Sub CollectClassifiedSpans( - tokens As IEnumerable(Of SyntaxToken), textSpan As TextSpan, list As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + tokens As IEnumerable(Of SyntaxToken), textSpan As TextSpan, list As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim worker = New Worker(textSpan, list, cancellationToken) For Each token In tokens @@ -34,7 +35,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification End Sub Friend Shared Sub CollectClassifiedSpans( - node As SyntaxNode, textSpan As TextSpan, list As ArrayBuilder(Of ClassifiedSpan), cancellationToken As CancellationToken) + node As SyntaxNode, textSpan As TextSpan, list As SegmentedList(Of ClassifiedSpan), cancellationToken As CancellationToken) Dim worker = New Worker(textSpan, list, cancellationToken) worker.ClassifyNode(node) End Sub diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index 167939982fd74..1e5dd6c7f899d 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -67,7 +67,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration End Function Friend Overrides Function DocumentationCommentTrivia(nodes As IEnumerable(Of SyntaxNode), trailingTrivia As SyntaxTriviaList, endOfLineString As String) As SyntaxNode - Dim node = SyntaxFactory.DocumentationCommentTrivia(SyntaxFactory.List(nodes)) + Dim node = SyntaxFactory.DocumentationCommentTrivia(CType(SyntaxFactory.List(nodes), SyntaxList(Of XmlNodeSyntax))) node = node.WithLeadingTrivia(SyntaxFactory.DocumentationCommentExteriorTrivia("''' ")). WithTrailingTrivia(node.GetTrailingTrivia()) @@ -77,7 +77,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Friend Overrides Function DocumentationCommentTriviaWithUpdatedContent(trivia As SyntaxTrivia, content As IEnumerable(Of SyntaxNode)) As SyntaxNode Dim documentationCommentTrivia = TryCast(trivia.GetStructure(), DocumentationCommentTriviaSyntax) If documentationCommentTrivia IsNot Nothing Then - Return SyntaxFactory.DocumentationCommentTrivia(SyntaxFactory.List(content)) + Return SyntaxFactory.DocumentationCommentTrivia(CType(SyntaxFactory.List(content), SyntaxList(Of XmlNodeSyntax))) End If Return Nothing @@ -307,7 +307,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Public Overrides Function ElementBindingExpression(arguments As IEnumerable(Of SyntaxNode)) As SyntaxNode Return SyntaxFactory.InvocationExpression(expression:=Nothing, - SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(arguments))) + SyntaxFactory.ArgumentList(CType(SyntaxFactory.SeparatedList(arguments), SeparatedSyntaxList(Of ArgumentSyntax)))) End Function ' parenthesize the left-side of a dot or target of an invocation if not unnecessary @@ -360,7 +360,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return SyntaxFactory.ObjectCreationExpression( attributeLists:=Nothing, DirectCast(typeName, TypeSyntax), - SyntaxFactory.ArgumentList(openParen, arguments, closeParen), + SyntaxFactory.ArgumentList(openParen, CType(arguments, SeparatedSyntaxList(Of ArgumentSyntax)), closeParen), initializer:=Nothing) End Function @@ -1453,7 +1453,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Private Function AsInterfaceMembers(nodes As IEnumerable(Of SyntaxNode)) As SyntaxList(Of StatementSyntax) If nodes IsNot Nothing Then - Return SyntaxFactory.List(nodes.Select(AddressOf AsInterfaceMember).Where(Function(n) n IsNot Nothing)) + Return CType(SyntaxFactory.List(nodes.Select(AddressOf AsInterfaceMember).Where(Function(n) n IsNot Nothing)), SyntaxList(Of StatementSyntax)) Else Return Nothing End If diff --git a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj index 26e74ab7a9e6c..94dad8fda12a1 100644 --- a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj +++ b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj @@ -3,7 +3,7 @@ Library - net6.0;netstandard2.0 + $(SourceBuildTargetFrameworks);netstandard2.0 full @@ -39,8 +39,10 @@ + + diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb index e822248d01aa4..d0a99da7660db 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Reducers/AbstractVisualBasicReducer.AbstractReductionRewriter.vb @@ -97,7 +97,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return newNode End If - If Not node.HasAnnotation(SimplificationHelpers.DontSimplifyAnnotation) Then + If Not node.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation) Then Dim simplifiedNode = simplifyFunc(node, _semanticModel, _simplificationOptions, CancellationToken) If simplifiedNode IsNot node Then _processedParentNodes.Add(parentNode) @@ -140,7 +140,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Return newToken End If - If Not token.HasAnnotation(SimplificationHelpers.DontSimplifyAnnotation) Then + If Not token.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation) Then Dim simplifiedToken = simplifyFunc(token, _semanticModel, _simplificationOptions, CancellationToken) If simplifiedToken <> token Then _processedParentNodes.Add(parentNode) diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/NameSimplifier.vb b/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/NameSimplifier.vb index 0897d716e8c3a..6220d00a10197 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/NameSimplifier.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/Simplifiers/NameSimplifier.vb @@ -369,8 +369,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification.Simplifiers If name.Parent.Kind = SyntaxKind.Attribute OrElse name.IsRightSideOfDot() Then Dim newIdentifierText = String.Empty - ' an attribute that should keep it (unnecessary "Attribute" suffix should be annotated with a DontSimplifyAnnotation - If identifierToken.HasAnnotation(SimplificationHelpers.DontSimplifyAnnotation) Then + ' an attribute that should keep it (unnecessary "Attribute" suffix should be annotated with a DoNotSimplifyAnnotation + If identifierToken.HasAnnotation(SimplificationHelpers.DoNotSimplifyAnnotation) Then newIdentifierText = identifierToken.ValueText + "Attribute" ElseIf identifierToken.ValueText.TryReduceAttributeSuffix(newIdentifierText) Then issueSpan = New TextSpan(name.Span.End - 9, 9) diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.Expander.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.Expander.vb index 1bdd54ae70f13..02dee1902fc90 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.Expander.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.Expander.vb @@ -588,7 +588,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification ' if the user already used the Attribute suffix in the attribute, we'll maintain it. If identifier.ValueText = name Then - identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation) + identifier = identifier.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation) End If End If End If diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb index 90244cf09532d..12ab6a61cad72 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb @@ -47,8 +47,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification If Me._isNodeOrTokenOutsideSimplifySpans(node) Then If Me._simplifyAllDescendants Then ' One of the ancestor nodes is within a simplification span, but this node Is outside all simplification spans. - ' Add DontSimplifyAnnotation to node to ensure it doesn't get simplified. - Return node.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation) + ' Add DoNotSimplifyAnnotation to node to ensure it doesn't get simplified. + Return node.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation) Else Return node End If @@ -87,8 +87,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification If Me._isNodeOrTokenOutsideSimplifySpans(token) Then If Me._simplifyAllDescendants Then ' One of the ancestor nodes is within a simplification span, but this token Is outside all simplification spans. - ' Add DontSimplifyAnnotation to token to ensure it doesn't get simplified. - Return token.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation) + ' Add DoNotSimplifyAnnotation to token to ensure it doesn't get simplified. + Return token.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation) Else Return token End If @@ -152,8 +152,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification If Me._isNodeOrTokenOutsideSimplifySpans(node) Then If Me._simplifyAllDescendants Then ' One of the ancestor nodes Is within a simplification span, but this node Is outside all simplification spans. - ' Add DontSimplifyAnnotation to node to ensure it doesn't get simplified. - Return node.WithAdditionalAnnotations(SimplificationHelpers.DontSimplifyAnnotation) + ' Add DoNotSimplifyAnnotation to node to ensure it doesn't get simplified. + Return node.WithAdditionalAnnotations(SimplificationHelpers.DoNotSimplifyAnnotation) Else Return node End If diff --git a/src/Workspaces/VisualBasicTest/CodeGeneration/AddImportsTests.vb b/src/Workspaces/VisualBasicTest/CodeGeneration/AddImportsTests.vb index 8d4d5dfb5cf6d..02ff8959c7b5f 100644 --- a/src/Workspaces/VisualBasicTest/CodeGeneration/AddImportsTests.vb +++ b/src/Workspaces/VisualBasicTest/CodeGeneration/AddImportsTests.vb @@ -163,7 +163,7 @@ End Class", useSymbolAnnotations) End Function - Public Async Function TestDontAddSystemImportFirst(useSymbolAnnotations As Boolean) As Task + Public Async Function TestDoNotAddSystemImportFirst(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports N @@ -399,7 +399,7 @@ End Class", useSymbolAnnotations) End Function - Public Async Function TestDontAddImportWithExisitingImportDifferentCase(useSymbolAnnotations As Boolean) As Task + Public Async Function TestDoNotAddImportWithExisitingImportDifferentCase(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports system.collections.generic diff --git a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb index 7491b2c1af87f..d58fc9f4ee983 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb @@ -4030,7 +4030,7 @@ End Module End Function - Public Async Function TestDontCrashOnMissingTokenWithComment() As Task + Public Async Function TestDoNotCrashOnMissingTokenWithComment() As Task Dim code =