diff --git a/NuGet.config b/NuGet.config index 045dcdbc3cf46..ee07d5df4b030 100644 --- a/NuGet.config +++ b/NuGet.config @@ -7,8 +7,9 @@ - + + 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.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 d96c1b6763db8..1ceac561bd177 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -20,7 +20,7 @@ 3.3.5-beta1.23204.1 - 8.0.0-preview.23180.2 + 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/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 5e9984f3f52e9..f00c32423d9f3 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -53,14 +53,63 @@ true - - net6.0 - net8.0;$(SourceBuildTargetFrameworks) - net8.0 + + + + + + $(NetPrevious) + $(SourceBuildToolsetTargetFramework) + $(NetCurrent);$(NetPrevious) + + - $(SourceBuildTargetFrameworks) - $(SourceBuildTargetFrameworksNetFx);net472 - + + + + $(NetCurrent) + $(SourceBuildToolsetTargetFramework) + $(NetCurrent) + + + + + + + net6.0 + $(NetCurrent);$(NetPrevious);$(SourceBuildToolsetTargetFramework) + $(SourceBuildToolsetTargetFrameworks) + + + + + + + net6.0 + $(SourceBuildToolsetTargetFramework);net7.0 + $(SourceBuildToolsetTargetFrameworks) + + + - - + diff --git a/global.json b/global.json index aa76203043e47..9edd9ee432ec2 100644 --- a/global.json +++ b/global.json @@ -15,4 +15,4 @@ "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23168.1", "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23168.1" } -} \ No newline at end of file +} diff --git a/src/Analyzers/CSharp/Analyzers/QualifyMemberAccess/CSharpQualifyMemberAccessDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/QualifyMemberAccess/CSharpQualifyMemberAccessDiagnosticAnalyzer.cs index 1facb9e93e74a..ea46c0a5b7809 100644 --- a/src/Analyzers/CSharp/Analyzers/QualifyMemberAccess/CSharpQualifyMemberAccessDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/QualifyMemberAccess/CSharpQualifyMemberAccessDiagnosticAnalyzer.cs @@ -30,18 +30,24 @@ protected override bool IsAlreadyQualifiedMemberAccess(ExpressionSyntax node) protected override bool CanMemberAccessBeQualified(ISymbol containingSymbol, SyntaxNode node) { if (node.GetAncestorOrThis() != null) - { return false; - } if (node.GetAncestorOrThis() != null) + return false; + + if (node.IsKind(SyntaxKind.BaseExpression)) + return false; + + if (IsInPropertyOrFieldInitialization(containingSymbol, node)) + return false; + + if (node.Parent is AssignmentExpressionSyntax { Parent: InitializerExpressionSyntax(SyntaxKind.ObjectInitializerExpression), Left: var left } && + left == node) { return false; } - return !(node.IsKind(SyntaxKind.BaseExpression) || - node.GetRequiredParent().GetRequiredParent().IsKind(SyntaxKind.ObjectInitializerExpression) || - IsInPropertyOrFieldInitialization(containingSymbol, node)); + return true; } private static bool IsInPropertyOrFieldInitialization(ISymbol containingSymbol, SyntaxNode node) diff --git a/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests.cs b/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests.cs index e057e9978f271..eb40a43bf3177 100644 --- a/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests.cs +++ b/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests.cs @@ -2014,7 +2014,42 @@ public void Bar() } } """, -CodeStyleOptions2.QualifyPropertyAccess); +CodeStyleOptions2.QualifyFieldAccess); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/22776")] + [WorkItem("https://github.com/dotnet/roslyn/issues/64374")] + public async Task DoReportToQualify_InObjectInitializer2() + { + await TestAsyncWithOption( + """ + public class C + { + public string Foo; + public void Bar() + { + var c = new C + { + Foo = [|Foo|] + }; + } + } + """, + """ + public class C + { + public string Foo; + public void Bar() + { + var c = new C + { + Foo = this.Foo + }; + } + } + """, + CodeStyleOptions2.QualifyFieldAccess); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/26893")] diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs index 80b8c8c7a8fa8..703877231ad66 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs @@ -9135,13 +9135,12 @@ void M(string[] ss) } """; - // Formatter bug tracked in https://github.com/dotnet/roslyn/issues/67516 var fixedSource = """ class C { void M(string[] ss) { - if (ss is [ []]) + if (ss is [[]]) { } @@ -9178,13 +9177,12 @@ void M(string[] ss) } """; - // Formatter bug tracked in https://github.com/dotnet/roslyn/issues/67516 var fixedSource = """ class C { void M(string[] ss) { - if (ss is [ []]) + if (ss is [[]]) { } diff --git a/src/Analyzers/Core/Analyzers/AnalyzersResources.resx b/src/Analyzers/Core/Analyzers/AnalyzersResources.resx index 7c0e9510cc6f7..df9dabaf42ade 100644 --- a/src/Analyzers/Core/Analyzers/AnalyzersResources.resx +++ b/src/Analyzers/Core/Analyzers/AnalyzersResources.resx @@ -352,6 +352,27 @@ Simplify LINQ expression + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Fix formatting @@ -376,4 +397,4 @@ Simplify check - \ No newline at end of file + diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs index 9fba7cf7c47c1..6877fbc7a39d5 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs @@ -19,12 +19,30 @@ internal abstract class AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer GetDescriptors(LocalizableSt { classificationIdDescriptor = CreateDescriptorWithId(IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId, EnforceOnBuildValues.RemoveUnnecessaryImports, titleAndMessage, isUnnecessary: true); generatedCodeClassificationIdDescriptor = CreateDescriptorWithId(IDEDiagnosticIds.RemoveUnnecessaryImportsDiagnosticId + "_gen", EnforceOnBuild.Never, titleAndMessage, isUnnecessary: true, isConfigurable: false); - return ImmutableArray.Create(s_fixableIdDescriptor, classificationIdDescriptor, generatedCodeClassificationIdDescriptor); + return ImmutableArray.Create(s_fixableIdDescriptor, s_enableGenerateDocumentationFileIdDescriptor, classificationIdDescriptor, generatedCodeClassificationIdDescriptor); } protected abstract ISyntaxFacts SyntaxFacts { get; } @@ -54,13 +72,13 @@ private static ImmutableArray GetDescriptors(LocalizableSt protected override void InitializeWorker(AnalysisContext context) { context.RegisterSemanticModelAction(AnalyzeSemanticModel); + context.RegisterCompilationAction(AnalyzeCompilation); } private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) { var tree = context.SemanticModel.SyntaxTree; var cancellationToken = context.CancellationToken; - var language = context.SemanticModel.Language; var unnecessaryImports = UnnecessaryImportsProvider.GetUnnecessaryImports(context.SemanticModel, cancellationToken); if (unnecessaryImports.Any()) @@ -87,6 +105,26 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) } } + private void AnalyzeCompilation(CompilationAnalysisContext context) + { + // Due to https://github.com/dotnet/roslyn/issues/41640, enabling this analyzer (IDE0005) on build requires users + // to enable generation of XML documentation comments. We detect if generation of XML documentation comments + // is disabled for this tree and IDE0005 diagnostics are being reported with effective severity "Warning" or "Error". + // If so, we report a special diagnostic that recommends the users to set "GenerateDocumentationFile" to "true" + // in their project file to enable IDE0005 on build. + + var compilation = context.Compilation; + var tree = compilation.SyntaxTrees.FirstOrDefault(tree => !GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocComment, context.CancellationToken)); + if (tree is null || tree.Options.DocumentationMode != DocumentationMode.None) + return; + + var effectiveSeverity = _classificationIdDescriptor.GetEffectiveSeverity(compilation.Options, tree, context.Options); + if (effectiveSeverity is ReportDiagnostic.Warn or ReportDiagnostic.Error) + { + context.ReportDiagnostic(Diagnostic.Create(s_enableGenerateDocumentationFileIdDescriptor, Location.None)); + } + } + private IEnumerable GetContiguousSpans(ImmutableArray nodes) { var syntaxFacts = this.SyntaxFacts; diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf index 12be2edb8ab0e..115a2de2cbcc1 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.cs.xlf @@ -37,6 +37,35 @@ Přidat modifikátor jen pro čtení + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Přidat kvalifikaci this nebo Me @@ -312,6 +341,16 @@ Odebrat nepoužité soukromé členy + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Zjednodušit výraz LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf index c0c81acd80f5d..efa62afd84999 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.de.xlf @@ -37,6 +37,35 @@ Modifizierer "readonly" hinzufügen + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Qualifizierung 'this' oder 'Me' hinzufügen @@ -312,6 +341,16 @@ Nicht verwendete private Member entfernen + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression LINQ-Ausdruck vereinfachen diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf index 1dab8b8ce0b05..51bbf57b6c93f 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.es.xlf @@ -37,6 +37,35 @@ Agregar modificador de solo lectura + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Agregar calificación 'this' o 'Me' @@ -312,6 +341,16 @@ Quitar miembros privados no utilizados + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Simplificar la expresión LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf index 1f7ce6e176138..e11c2ad4c8d9f 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.fr.xlf @@ -37,6 +37,35 @@ Ajouter un modificateur readonly + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Ajoutez la qualification « this » ou « Me ». @@ -312,6 +341,16 @@ Supprimer les membres privés non utilisés + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Simplifier l'expression LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf index 9306efc27203f..d7dc1d890cd13 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.it.xlf @@ -37,6 +37,35 @@ Aggiungi modificatore readonly + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Aggiunge la qualificazione 'this' o 'Me' @@ -312,6 +341,16 @@ Rimuovi i membri privati inutilizzati + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Semplifica l'espressione LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf index 36577b2a7d4bd..1b5686d68195f 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ja.xlf @@ -37,6 +37,35 @@ 読み取り専用修飾子を追加します + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification this' または 'Me' 修飾子を追加する @@ -312,6 +341,16 @@ 使用されていないプライベート メンバーを削除する + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression LINQ 式を簡略化する diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf index 3640249d7da74..01d6e219e55b0 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ko.xlf @@ -37,6 +37,35 @@ 읽기 전용 한정자 추가 + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification 'this' 또는 'Me' 한정자를 추가합니다. @@ -312,6 +341,16 @@ 사용되지 않는 private 멤버 제거 + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression LINQ 식 단순화 diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf index a52bd4f1e65f0..898cd3d967f4d 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pl.xlf @@ -37,6 +37,35 @@ Dodaj modyfikator tylko do odczytu + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Dodaj kwalifikacje „this” lub „Me” @@ -312,6 +341,16 @@ Usuń nieużywane prywatne składowe + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Uprość wyrażenie LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf index 483beb3e85420..be6f44229d358 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.pt-BR.xlf @@ -37,6 +37,35 @@ Adicionar modificador somente leitura + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Adicionar a qualificação 'este' ou 'Eu' @@ -312,6 +341,16 @@ Remover membros privados não utilizados + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Simplificar a expressão LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf index 90548d064d0a1..39e2c548d565a 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.ru.xlf @@ -37,6 +37,35 @@ Добавить модификатор только для чтения + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification Добавьте квалификацию \"this\" или \"Me\" @@ -312,6 +341,16 @@ Удалите неиспользуемые закрытые члены + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression Упростите выражение LINQ diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf index 9dd7aaa35b7ad..adbd53f35275d 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.tr.xlf @@ -37,6 +37,35 @@ Salt okunur değiştirici ekle + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification 'this' veya 'Me' niteliği ekle @@ -312,6 +341,16 @@ Kullanılmayan özel üyeleri kaldır + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression LINQ ifadesini basitleştir diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf index 7ded05062a88b..905c03f297c1d 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf @@ -37,6 +37,35 @@ 添加只读修饰符 + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification 添加 “this” 或 “Me” 限定 @@ -312,6 +341,16 @@ 删除未使用的私有成员 + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression 简化 LINQ 表达式 diff --git a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf index 3ee53e76fea0a..7859c31daefe8 100644 --- a/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/Core/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf @@ -37,6 +37,35 @@ 新增唯讀修飾元 + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + Add the following PropertyGroup to your MSBuild project file to enable IDE0005 (Remove unnecessary usings/imports) on build: + <PropertyGroup> + <!-- + Make sure any documentation comments which are included in code get checked for syntax during the build, but do + not report warnings for missing comments. + CS1573: Parameter 'parameter' has no matching param tag in the XML comment for 'parameter' (but other parameters do) + CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' + CS1712: Type parameter 'type_parameter' has no matching typeparam tag in the XML comment on 'type_or_member' (but other type parameters do) + --> + <GenerateDocumentationFile>True</GenerateDocumentationFile> + <NoWarn>$(NoWarn),1573,1591,1712</NoWarn> + </PropertyGroup> + + + Add 'this' or 'Me' qualification 新增 'this' 或 'Me' 限定性條件。 @@ -312,6 +341,16 @@ 刪除未使用的私用成員 + + Set MSBuild property 'GenerateDocumentationFile' to 'true' + Set MSBuild property 'GenerateDocumentationFile' to 'true' + + + + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build + + Simplify LINQ expression 簡化 LINQ 運算式 diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index ca2362131ea6c..a47b97af47889 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -739,7 +739,7 @@ internal static void ReportDiagnosticsIfObsoleteInternal(BindingDiagnosticBag di } } - internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBag diagnostics, MethodSymbol symbol, Location location, bool isDelegateConversion) + internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBag diagnostics, MethodSymbol symbol, SyntaxNodeOrToken syntax, bool isDelegateConversion) { var unmanagedCallersOnlyAttributeData = symbol.GetUnmanagedCallersOnlyAttributeData(forceComplete: false); if (unmanagedCallersOnlyAttributeData != null) @@ -747,13 +747,14 @@ internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBa // Either we haven't yet bound the attributes of this method, or there is an UnmanagedCallersOnly present. // In the former case, we use a lazy diagnostic that may end up being ignored later, to avoid causing a // binding cycle. + Debug.Assert(syntax.GetLocation() != null); diagnostics.Add(unmanagedCallersOnlyAttributeData == UnmanagedCallersOnlyAttributeData.Uninitialized - ? (DiagnosticInfo)new LazyUnmanagedCallersOnlyMethodCalledDiagnosticInfo(symbol, isDelegateConversion) + ? new LazyUnmanagedCallersOnlyMethodCalledDiagnosticInfo(symbol, isDelegateConversion) : new CSDiagnosticInfo(isDelegateConversion ? ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeConvertedToDelegate : ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly, symbol), - location); + syntax.GetLocation()!); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs index d8373936fba69..94c4679db30e1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs @@ -20,7 +20,7 @@ internal partial class Binder { private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureAnonymousTypes.CheckFeatureAvailability(diagnostics, node, node.NewKeyword.GetLocation()); + MessageID.IDS_FeatureAnonymousTypes.CheckFeatureAvailability(diagnostics, node.NewKeyword); // prepare var initializers = node.Initializers; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index e8f3afa1ee7bd..4dc2823f36c76 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -57,8 +57,7 @@ internal static void BindAttributeTypes( // Check the attribute type (unless the attribute type is already an error). if (boundTypeSymbol.TypeKind != TypeKind.Error) { - var location = attributeToBind.Name.GetLocation(); - binder.CheckDisallowedAttributeDependentType(boundType, location, diagnostics); + binder.CheckDisallowedAttributeDependentType(boundType, attributeToBind.Name, diagnostics); } boundAttributeTypes[i] = boundTypeSymbol; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 9f234db5e7507..bc4ae144fe345 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -18,7 +18,7 @@ internal partial class Binder { private BoundExpression BindAwait(AwaitExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, node, node.AwaitKeyword.GetLocation()); + MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, node.AwaitKeyword); BoundExpression expression = BindRValueWithoutTargetType(node.Expression, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 525e00a5d611b..9f4e83b3466f1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1477,7 +1477,7 @@ private bool MethodGroupConversionHasErrors( CheckValidScopedMethodConversion(syntax, selectedMethod, delegateOrFuncPtrType, isExtensionMethod, diagnostics); if (!isAddressOf) { - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, selectedMethod, location, isDelegateConversion: true); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, selectedMethod, syntax, isDelegateConversion: true); } ReportDiagnosticsIfObsolete(diagnostics, selectedMethod, syntax, hasBaseReceiver: false); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index c4b5a5137b516..51ba6cda8f794 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -835,7 +835,7 @@ private BoundExpression BindScopedType(ExpressionSyntax node, BindingDiagnosticB private BoundExpression BindThrowExpression(ThrowExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureThrowExpression.CheckFeatureAvailability(diagnostics, node, node.ThrowKeyword.GetLocation()); + MessageID.IDS_FeatureThrowExpression.CheckFeatureAvailability(diagnostics, node.ThrowKeyword); bool hasErrors = node.HasErrors; if (!IsThrowExpressionInProperContext(node)) @@ -1358,29 +1358,29 @@ private BoundExpression BindTypeOf(TypeOfExpressionSyntax node, BindingDiagnosti } /// Called when an "attribute-dependent" type such as 'dynamic', 'string?', etc. is not permitted. - private void CheckDisallowedAttributeDependentType(TypeWithAnnotations typeArgument, Location errorLocation, BindingDiagnosticBag diagnostics) + private void CheckDisallowedAttributeDependentType(TypeWithAnnotations typeArgument, NameSyntax attributeName, BindingDiagnosticBag diagnostics) { typeArgument.VisitType(type: null, static (typeWithAnnotations, arg, _) => { - var (errorLocation, diagnostics) = arg; + var (attributeName, diagnostics) = arg; var type = typeWithAnnotations.Type; if (type.IsDynamic() || (typeWithAnnotations.NullableAnnotation.IsAnnotated() && !type.IsValueType) || type.IsNativeIntegerWrapperType || (type.IsTupleType && !type.TupleElementNames.IsDefault)) { - diagnostics.Add(ErrorCode.ERR_AttrDependentTypeNotAllowed, errorLocation, type); + diagnostics.Add(ErrorCode.ERR_AttrDependentTypeNotAllowed, attributeName, type); return true; } if (type.IsUnboundGenericType() || type.Kind == SymbolKind.TypeParameter) { - diagnostics.Add(ErrorCode.ERR_AttrTypeArgCannotBeTypeVar, errorLocation, type); + diagnostics.Add(ErrorCode.ERR_AttrTypeArgCannotBeTypeVar, attributeName, type); return true; } return false; - }, typePredicate: null, arg: (errorLocation, diagnostics)); + }, typePredicate: null, arg: (attributeName, diagnostics)); } private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, BindingDiagnosticBag diagnostics) @@ -1441,7 +1441,7 @@ internal static ConstantValue GetConstantSizeOf(TypeSymbol type) private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureDefault.CheckFeatureAvailability(diagnostics, node, node.Keyword.GetLocation()); + MessageID.IDS_FeatureDefault.CheckFeatureAvailability(diagnostics, node.Keyword); TypeWithAnnotations typeWithAnnotations = this.BindType(node.Type, diagnostics, out AliasSymbol alias); var typeExpression = new BoundTypeExpression(node.Type, aliasOpt: alias, typeWithAnnotations); @@ -2052,14 +2052,17 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind bool isUsedBeforeDeclaration(SimpleNameSyntax node, LocalSymbol localSymbol) { - Location localSymbolLocation = localSymbol.GetFirstLocation(); + if (!localSymbol.HasSourceLocation) + return false; - if (node.SyntaxTree == localSymbolLocation.SourceTree) - { - return node.SpanStart < localSymbolLocation.SourceSpan.Start; - } + var declarator = localSymbol.GetDeclaratorSyntax(); - return false; + // trivial position check, before more costly tree check (which requires walking up the nodes). Most + // code is correct, so this check is expected to succeed nearly every time. + if (node.SpanStart >= declarator.SpanStart) + return false; + + return node.SyntaxTree == declarator.SyntaxTree; } } @@ -2108,19 +2111,18 @@ private BoundExpression SynthesizeReceiver(SyntaxNode node, Symbol member, Bindi (currentType.IsInterface && (declaringType.IsObjectType() || currentType.AllInterfacesNoUseSiteDiagnostics.Contains(declaringType)))) { bool hasErrors = false; - if (EnclosingNameofArgument != node) + if (!IsInsideNameof || (EnclosingNameofArgument != node && !node.IsFeatureEnabled(MessageID.IDS_FeatureInstanceMemberInNameof))) { + DiagnosticInfo diagnosticInfoOpt = null; if (InFieldInitializer && !currentType.IsScriptClass) { //can't access "this" in field initializers - Error(diagnostics, ErrorCode.ERR_FieldInitRefNonstatic, node, member); - hasErrors = true; + diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_FieldInitRefNonstatic, member); } else if (InConstructorInitializer || InAttributeArgument) { //can't access "this" in constructor initializers or attribute arguments - Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, member); - hasErrors = true; + diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, member); } else { @@ -2132,12 +2134,24 @@ private BoundExpression SynthesizeReceiver(SyntaxNode node, Symbol member, Bindi if (!locationIsInstanceMember) { // error CS0120: An object reference is required for the non-static field, method, or property '{0}' - Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, member); - hasErrors = true; + diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, member); } } - hasErrors = hasErrors || IsRefOrOutThisParameterCaptured(node, diagnostics); + diagnosticInfoOpt ??= GetDiagnosticIfRefOrOutThisParameterCaptured(); + hasErrors = diagnosticInfoOpt is not null; + + if (hasErrors) + { + if (IsInsideNameof) + { + CheckFeatureAvailability(node, MessageID.IDS_FeatureInstanceMemberInNameof, diagnostics); + } + else + { + Error(diagnostics, diagnosticInfoOpt, node); + } + } } return ThisReference(node, currentType, hasErrors, wasCompilerGenerated: true); @@ -2309,20 +2323,35 @@ private BoundThisReference ThisReference(SyntaxNode node, NamedTypeSymbol thisTy return new BoundThisReference(node, thisTypeOpt ?? CreateErrorType(), hasErrors) { WasCompilerGenerated = wasCompilerGenerated }; } +#nullable enable private bool IsRefOrOutThisParameterCaptured(SyntaxNodeOrToken thisOrBaseToken, BindingDiagnosticBag diagnostics) { - ParameterSymbol thisSymbol = this.ContainingMemberOrLambda.EnclosingThisSymbol(); - // If there is no this parameter, then it is definitely not captured and - // any diagnostic would be cascading. - if ((object)thisSymbol != null && thisSymbol.ContainingSymbol != ContainingMemberOrLambda && thisSymbol.RefKind != RefKind.None) + if (GetDiagnosticIfRefOrOutThisParameterCaptured() is { } diagnosticInfo) { - Error(diagnostics, ErrorCode.ERR_ThisStructNotInAnonMeth, thisOrBaseToken); + var location = thisOrBaseToken.GetLocation(); + Debug.Assert(location is not null); + Error(diagnostics, diagnosticInfo, location); return true; } return false; } + private DiagnosticInfo? GetDiagnosticIfRefOrOutThisParameterCaptured() + { + Debug.Assert(this.ContainingMemberOrLambda is not null); + ParameterSymbol? thisSymbol = this.ContainingMemberOrLambda.EnclosingThisSymbol(); + // If there is no this parameter, then it is definitely not captured and + // any diagnostic would be cascading. + if (thisSymbol is not null && thisSymbol.ContainingSymbol != ContainingMemberOrLambda && thisSymbol.RefKind != RefKind.None) + { + return new CSDiagnosticInfo(ErrorCode.ERR_ThisStructNotInAnonMeth); + } + + return null; + } +#nullable disable + private BoundBaseReference BindBase(BaseExpressionSyntax node, BindingDiagnosticBag diagnostics) { bool hasErrors = false; @@ -2901,12 +2930,12 @@ private void BindArgumentAndName( private BoundExpression BindArgumentValue(BindingDiagnosticBag diagnostics, ArgumentSyntax argumentSyntax, bool allowArglist, RefKind refKind) { if (argumentSyntax.RefKindKeyword.IsKind(SyntaxKind.InKeyword)) - MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, argumentSyntax, argumentSyntax.RefKindKeyword.GetLocation()); + MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, argumentSyntax.RefKindKeyword); if (argumentSyntax.Expression.Kind() == SyntaxKind.DeclarationExpression) { if (argumentSyntax.RefKindKeyword.IsKind(SyntaxKind.OutKeyword)) - MessageID.IDS_FeatureOutVar.CheckFeatureAvailability(diagnostics, argumentSyntax, argumentSyntax.RefKindKeyword.GetLocation()); + MessageID.IDS_FeatureOutVar.CheckFeatureAvailability(diagnostics, argumentSyntax.RefKindKeyword); var declarationExpression = (DeclarationExpressionSyntax)argumentSyntax.Expression; if (declarationExpression.IsOutDeclaration()) @@ -3436,7 +3465,7 @@ private BoundExpression BindArrayDimension(ExpressionSyntax dimension, BindingDi private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { // See BindArrayCreationExpression method above for implicitly typed array creation SPEC. - MessageID.IDS_FeatureImplicitArray.CheckFeatureAvailability(diagnostics, node, node.NewKeyword.GetLocation()); + MessageID.IDS_FeatureImplicitArray.CheckFeatureAvailability(diagnostics, node.NewKeyword); InitializerExpressionSyntax initializer = node.Initializer; int rank = node.Commas.Count + 1; @@ -3907,7 +3936,7 @@ private bool ReportBadStackAllocPosition(SyntaxNode node, BindingDiagnosticBag d inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalCSharp73SpanStackAllocPosition(); if (!inLegalPosition) { - MessageID.IDS_FeatureNestedStackalloc.CheckFeatureAvailability(diagnostics, node, node.GetFirstToken().GetLocation()); + MessageID.IDS_FeatureNestedStackalloc.CheckFeatureAvailability(diagnostics, node.GetFirstToken()); } } @@ -3989,7 +4018,7 @@ private BoundExpression BindStackAllocWithInitializer( { Debug.Assert(node.IsKind(SyntaxKind.ImplicitStackAllocArrayCreationExpression) || node.IsKind(SyntaxKind.StackAllocArrayCreationExpression)); - MessageID.IDS_FeatureStackAllocInitializer.CheckFeatureAvailability(diagnostics, node, stackAllocKeyword.GetLocation()); + MessageID.IDS_FeatureStackAllocInitializer.CheckFeatureAvailability(diagnostics, stackAllocKeyword); if (boundInitExprOpt.IsDefault) { @@ -4435,7 +4464,7 @@ constructor is not SynthesizedPrimaryConstructor && private BoundExpression BindImplicitObjectCreationExpression(ImplicitObjectCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureImplicitObjectCreation.CheckFeatureAvailability(diagnostics, node, node.NewKeyword.GetLocation()); + MessageID.IDS_FeatureImplicitObjectCreation.CheckFeatureAvailability(diagnostics, node.NewKeyword); var arguments = AnalyzedArguments.GetInstance(); BindArgumentsAndNames(node.ArgumentList, diagnostics, arguments, allowArglist: true); @@ -4858,7 +4887,7 @@ private BoundObjectInitializerExpression BindObjectInitializerExpression( Debug.Assert((object)initializerType != null); if (initializerSyntax.Kind() == SyntaxKind.ObjectInitializerExpression) - MessageID.IDS_FeatureObjectInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax, initializerSyntax.OpenBraceToken.GetLocation()); + MessageID.IDS_FeatureObjectInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax.OpenBraceToken); // We use a location specific binder for binding object initializer field/property access to generate object initializer specific diagnostics: // 1) CS1914 (ERR_StaticMemberInObjectInitializer) @@ -5004,8 +5033,7 @@ private BoundExpression BindObjectInitializerMember( { var implicitIndexing = (ImplicitElementAccessSyntax)leftSyntax; - MessageID.IDS_FeatureDictionaryInitializer.CheckFeatureAvailability( - diagnostics, implicitIndexing, implicitIndexing.ArgumentList.OpenBracketToken.GetLocation()); + MessageID.IDS_FeatureDictionaryInitializer.CheckFeatureAvailability(diagnostics, implicitIndexing.ArgumentList.OpenBracketToken); boundMember = BindElementAccess(implicitIndexing, implicitReceiver, implicitIndexing.ArgumentList, diagnostics); @@ -5355,7 +5383,7 @@ private BoundCollectionInitializerExpression BindCollectionInitializerExpression Debug.Assert(initializerSyntax.Expressions.Any()); Debug.Assert((object)initializerType != null); - MessageID.IDS_FeatureCollectionInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax, initializerSyntax.OpenBraceToken.GetLocation()); + MessageID.IDS_FeatureCollectionInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax.OpenBraceToken); var initializerBuilder = ArrayBuilder.GetInstance(); @@ -6297,7 +6325,7 @@ private BoundLiteral BindLiteralConstant(LiteralExpressionSyntax node, BindingDi if (node.Token.Kind() is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) { - MessageID.IDS_FeatureRawStringLiterals.CheckFeatureAvailability(diagnostics, node, node.Location); + MessageID.IDS_FeatureRawStringLiterals.CheckFeatureAvailability(diagnostics, node); } return new BoundLiteral(node, cv, type); @@ -7747,10 +7775,17 @@ private bool CheckInstanceOrStatic( { if (instanceReceiver == true) { - ErrorCode errorCode = this.Flags.Includes(BinderFlags.ObjectInitializerMember) ? - ErrorCode.ERR_StaticMemberInObjectInitializer : - ErrorCode.ERR_ObjectProhibited; - Error(diagnostics, errorCode, node, symbol); + if (!IsInsideNameof) + { + ErrorCode errorCode = this.Flags.Includes(BinderFlags.ObjectInitializerMember) ? + ErrorCode.ERR_StaticMemberInObjectInitializer : + ErrorCode.ERR_ObjectProhibited; + Error(diagnostics, errorCode, node, symbol); + } + else if (CheckFeatureAvailability(node, MessageID.IDS_FeatureInstanceMemberInNameof, diagnostics)) + { + return false; + } resultKind = LookupResultKind.StaticInstanceMismatch; return true; } @@ -9366,7 +9401,7 @@ internal static bool ReportDelegateInvokeUseSiteDiagnostic(BindingDiagnosticBag private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccessExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureNullPropagatingOperator.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureNullPropagatingOperator.CheckFeatureAvailability(diagnostics, node.OperatorToken); BoundExpression receiver = BindConditionalAccessReceiver(node, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index fea8c26e413d0..bcd4ef05874b1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1109,7 +1109,7 @@ private BoundCall BindInvocationExpressionContinued( bool hasBaseReceiver = receiver != null && receiver.Kind == BoundKind.BaseReference; ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver); - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node.Location, isDelegateConversion: false); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false); // No use site errors, but there could be use site warnings. // If there are any use site warnings, they have already been reported by overload resolution. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 5f818e6af0cb7..ed1cbf9c8f28f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -56,7 +56,7 @@ private UnboundLambda AnalyzeAnonymousFunction( if (syntax is LambdaExpressionSyntax lambdaSyntax) { - MessageID.IDS_FeatureLambda.CheckFeatureAvailability(diagnostics, syntax, lambdaSyntax.ArrowToken.GetLocation()); + MessageID.IDS_FeatureLambda.CheckFeatureAvailability(diagnostics, lambdaSyntax.ArrowToken); checkAttributes(syntax, lambdaSyntax.AttributeLists, diagnostics); } @@ -86,7 +86,7 @@ private UnboundLambda AnalyzeAnonymousFunction( // delegate (int x) { } // delegate { } var anon = (AnonymousMethodExpressionSyntax)syntax; - MessageID.IDS_FeatureAnonDelegates.CheckFeatureAvailability(diagnostics, anon, anon.DelegateKeyword.GetLocation()); + MessageID.IDS_FeatureAnonDelegates.CheckFeatureAvailability(diagnostics, anon.DelegateKeyword); hasSignature = anon.ParameterList != null; if (hasSignature) @@ -105,12 +105,12 @@ private UnboundLambda AnalyzeAnonymousFunction( { if (modifier.IsKind(SyntaxKind.AsyncKeyword)) { - MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, syntax, modifier.GetLocation()); + MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, modifier); isAsync = true; } else if (modifier.IsKind(SyntaxKind.StaticKeyword)) { - MessageID.IDS_FeatureStaticAnonymousFunction.CheckFeatureAvailability(diagnostics, syntax, modifier.GetLocation()); + MessageID.IDS_FeatureStaticAnonymousFunction.CheckFeatureAvailability(diagnostics, modifier); isStatic = true; } } @@ -155,7 +155,7 @@ private UnboundLambda AnalyzeAnonymousFunction( } else { - MessageID.IDS_FeatureLambdaOptionalParameters.CheckFeatureAvailability(diagnostics, syntax, p.Default.EqualsToken.GetLocation()); + MessageID.IDS_FeatureLambdaOptionalParameters.CheckFeatureAvailability(diagnostics, p.Default.EqualsToken); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 1ac2909d7eec8..0653d993db96e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2407,7 +2407,7 @@ private bool CheckConstraintLanguageVersionAndRuntimeSupportForOperator(SyntaxNo private BoundExpression BindSuppressNullableWarningExpression(PostfixUnaryExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureNullableReferenceTypes.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureNullableReferenceTypes.CheckFeatureAvailability(diagnostics, node.OperatorToken); var expr = BindExpression(node.Operand, diagnostics); switch (expr.Kind) @@ -4089,7 +4089,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node, private BoundExpression BindNullCoalescingAssignmentOperator(AssignmentExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureCoalesceAssignmentExpression.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureCoalesceAssignmentExpression.CheckFeatureAvailability(diagnostics, node.OperatorToken); BoundExpression leftOperand = BindValue(node.Left, diagnostics, BindValueKind.CompoundAssignment); ReportSuppressionIfNeeded(leftOperand, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 34a5f31066138..eae830cc46812 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -17,7 +17,7 @@ partial class Binder { private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node, node.IsKeyword.GetLocation()); + MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node.IsKeyword); BoundExpression expression = BindRValueWithoutTargetType(node.Expression, diagnostics); bool hasErrors = IsOperandErrors(node, ref expression, diagnostics); @@ -144,7 +144,7 @@ private BoundExpression BindSwitchExpression(SwitchExpressionSyntax node, Bindin { RoslynDebug.Assert(node is not null); - MessageID.IDS_FeatureRecursivePatterns.CheckFeatureAvailability(diagnostics, node, node.SwitchKeyword.GetLocation()); + MessageID.IDS_FeatureRecursivePatterns.CheckFeatureAvailability(diagnostics, node.SwitchKeyword); Binder? switchBinder = this.GetBinder(node); RoslynDebug.Assert(switchBinder is { }); @@ -194,7 +194,7 @@ private BoundPattern BindParenthesizedPattern( BindingDiagnosticBag diagnostics, bool underIsPattern) { - MessageID.IDS_FeatureParenthesizedPattern.CheckFeatureAvailability(diagnostics, node, node.OpenParenToken.GetLocation()); + MessageID.IDS_FeatureParenthesizedPattern.CheckFeatureAvailability(diagnostics, node.OpenParenToken); return BindPattern(node.Pattern, inputType, permitDesignations, hasErrors, diagnostics, underIsPattern); } @@ -1088,7 +1088,7 @@ deconstructMethod is null && } else if (subPattern.ExpressionColon != null) { - MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, subPattern, subPattern.ExpressionColon.ColonToken.GetLocation()); + MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, subPattern.ExpressionColon.ColonToken); diagnostics.Add(ErrorCode.ERR_IdentifierExpected, subPattern.ExpressionColon.Expression.Location); } @@ -1466,7 +1466,7 @@ private ImmutableArray BindPropertyPatternClause( foreach (SubpatternSyntax p in node.Subpatterns) { if (p.ExpressionColon is ExpressionColonSyntax) - MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, p, p.ExpressionColon.ColonToken.GetLocation()); + MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, p.ExpressionColon.ColonToken); ExpressionSyntax? expr = p.ExpressionColon?.Expression; PatternSyntax pattern = p.Pattern; @@ -1626,7 +1626,7 @@ private BoundPattern BindRelationalPattern( bool hasErrors, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureRelationalPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureRelationalPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); BoundExpression value = BindExpressionForPattern(inputType, node.Expression, ref hasErrors, diagnostics, out var constantValueOpt, out _, out Conversion patternConversion); ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(node.Expression, diagnostics, ref hasErrors); @@ -1719,7 +1719,7 @@ private BoundPattern BindUnaryPattern( BindingDiagnosticBag diagnostics, bool underIsPattern) { - MessageID.IDS_FeatureNotPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureNotPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); bool permitDesignations = underIsPattern; // prevent designators under 'not' except under an is-pattern var subPattern = BindPattern(node.Pattern, inputType, permitDesignations, hasErrors, diagnostics, underIsPattern); @@ -1736,7 +1736,7 @@ private BoundPattern BindBinaryPattern( bool isDisjunction = node.Kind() == SyntaxKind.OrPattern; if (isDisjunction) { - MessageID.IDS_FeatureOrPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureOrPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); permitDesignations = false; // prevent designators under 'or' var left = BindPattern(node.Left, inputType, permitDesignations, hasErrors, diagnostics); @@ -1821,7 +1821,7 @@ static void collectCandidates(BoundPattern pat, ArrayBuilder candida } else { - MessageID.IDS_FeatureAndPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureAndPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); var left = BindPattern(node.Left, inputType, permitDesignations, hasErrors, diagnostics); var right = BindPattern(node.Right, left.NarrowedType, permitDesignations, hasErrors, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 4a53b0d9ececd..1b4e98e15e444 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -23,7 +23,7 @@ internal partial class Binder internal BoundExpression BindQuery(QueryExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureQueryExpression.CheckFeatureAvailability(diagnostics, node, node.FromClause.FromKeyword.GetLocation()); + MessageID.IDS_FeatureQueryExpression.CheckFeatureAvailability(diagnostics, node.FromClause.FromKeyword); var fromClause = node.FromClause; var boundFromExpression = BindLeftOfPotentialColorColorMemberAccess(fromClause.Expression, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index cc10d881f3e13..116801cc47844 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -215,7 +215,7 @@ private BoundStatement BindFixedStatementParts(FixedStatementSyntax node, Bindin private void CheckRequiredLangVersionForIteratorMethods(YieldStatementSyntax statement, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureIterators.CheckFeatureAvailability(diagnostics, statement, statement.YieldKeyword.GetLocation()); + MessageID.IDS_FeatureIterators.CheckFeatureAvailability(diagnostics, statement.YieldKeyword); var method = (MethodSymbol)this.ContainingMemberOrLambda; if (method.IsAsync) @@ -550,7 +550,7 @@ private BoundStatement BindGoto(GotoStatementSyntax node, BindingDiagnosticBag d private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureLocalFunctions.CheckFeatureAvailability(diagnostics, node, node.Identifier.GetLocation()); + MessageID.IDS_FeatureLocalFunctions.CheckFeatureAvailability(diagnostics, node.Identifier); // already defined symbol in containing block var localSymbol = this.LookupLocalFunction(node.Identifier); @@ -595,9 +595,9 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n foreach (var modifier in node.Modifiers) { if (modifier.IsKind(SyntaxKind.StaticKeyword)) - MessageID.IDS_FeatureStaticLocalFunctions.CheckFeatureAvailability(diagnostics, node, modifier.GetLocation()); + MessageID.IDS_FeatureStaticLocalFunctions.CheckFeatureAvailability(diagnostics, modifier); else if (modifier.IsKind(SyntaxKind.ExternKeyword)) - MessageID.IDS_FeatureExternLocalFunctions.CheckFeatureAvailability(diagnostics, node, modifier.GetLocation()); + MessageID.IDS_FeatureExternLocalFunctions.CheckFeatureAvailability(diagnostics, modifier); } return new BoundLocalFunctionStatement(node, localSymbol, blockBody, expressionBody, hasErrors); @@ -1426,7 +1426,7 @@ private BoundExpression BindAssignment(AssignmentExpressionSyntax node, BindingD var lhsKind = isRef ? BindValueKind.RefAssignable : BindValueKind.Assignable; if (isRef) - MessageID.IDS_FeatureRefReassignment.CheckFeatureAvailability(diagnostics, node.Right, node.Right.GetFirstToken().GetLocation()); + MessageID.IDS_FeatureRefReassignment.CheckFeatureAvailability(diagnostics, node.Right.GetFirstToken()); var op1 = BindValue(node.Left, diagnostics, lhsKind); ReportSuppressionIfNeeded(op1, diagnostics); @@ -3247,7 +3247,7 @@ private BoundCatchBlock BindCatchBlock(CatchClauseSyntax node, ArrayBuilder throw ExceptionUtilities.UnexpectedValue(expressionBody.Parent.Kind()), }; - messageId?.CheckFeatureAvailability(diagnostics, expressionBody, expressionBody.ArrowToken.GetLocation()); + messageId?.CheckFeatureAvailability(diagnostics, expressionBody.ArrowToken); Binder bodyBinder = this.GetBinder(expressionBody); Debug.Assert(bodyBinder != null); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index fb06a4e052467..8f668fc7684e4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -444,7 +444,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS case SyntaxKind.FunctionPointerType: var functionPointerTypeSyntax = (FunctionPointerTypeSyntax)syntax; - MessageID.IDS_FeatureFunctionPointers.CheckFeatureAvailability(diagnostics, syntax, functionPointerTypeSyntax.DelegateKeyword.GetLocation()); + MessageID.IDS_FeatureFunctionPointers.CheckFeatureAvailability(diagnostics, functionPointerTypeSyntax.DelegateKeyword); if (GetUnsafeDiagnosticInfo(sizeOfTypeOpt: null) is CSDiagnosticInfo info) { @@ -535,7 +535,7 @@ void reportNullableReferenceTypesIfNeeded(SyntaxToken questionToken, TypeWithAnn NamespaceOrTypeOrAliasSymbolWithAnnotations bindNullable() { var nullableSyntax = (NullableTypeSyntax)syntax; - MessageID.IDS_FeatureNullable.CheckFeatureAvailability(diagnostics, nullableSyntax, nullableSyntax.QuestionToken.GetLocation()); + MessageID.IDS_FeatureNullable.CheckFeatureAvailability(diagnostics, nullableSyntax.QuestionToken); TypeSyntax typeArgumentSyntax = nullableSyntax.ElementType; TypeWithAnnotations typeArgument = BindType(typeArgumentSyntax, diagnostics, basesBeingResolved); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs b/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs index 9bb42e8b61beb..1ff2935f76237 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs @@ -14,7 +14,7 @@ internal partial class Binder { private BoundExpression BindWithExpression(WithExpressionSyntax syntax, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureRecords.CheckFeatureAvailability(diagnostics, syntax, syntax.WithKeyword.GetLocation()); + MessageID.IDS_FeatureRecords.CheckFeatureAvailability(diagnostics, syntax.WithKeyword); var receiver = BindRValueWithoutTargetType(syntax.Expression, diagnostics); var receiverType = receiver.Type; diff --git a/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs b/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs index b1aab32456052..d69391916db66 100644 --- a/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs +++ b/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs @@ -160,6 +160,12 @@ internal CSDiagnosticInfo Add(ErrorCode code, Location location) return info; } + internal CSDiagnosticInfo Add(ErrorCode code, SyntaxNode syntax, params object[] args) + => Add(code, syntax.Location, args); + + internal CSDiagnosticInfo Add(ErrorCode code, SyntaxToken syntax, params object[] args) + => Add(code, syntax.GetLocation()!, args); + internal CSDiagnosticInfo Add(ErrorCode code, Location location, params object[] args) { var info = new CSDiagnosticInfo(code, args); diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs index 4a7f3b64ae7a4..400e35aa309b5 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -37,25 +38,23 @@ namespace Microsoft.CodeAnalysis.CSharp /// is performed only after testing that the value is of that type). /// /// - /// In order to build this automaton, we start (in - /// - /// by computing a description of the initial state in a , and then - /// for each such state description we decide what the test or evaluation will be at - /// that state, and compute the successor state descriptions. - /// A state description represented by a is a collection of partially matched - /// cases represented - /// by , in which some number of the tests have already been performed - /// for each case. - /// When we have computed descriptions for all of the states, we create a new - /// for each of them, containing - /// the state transitions (including the test to perform at each node and the successor nodes) but - /// not the state descriptions. A containing this - /// set of nodes becomes part of the bound nodes (e.g. in and - /// ) and is used for semantic analysis and lowering. + /// In order to build this automaton, we start (in ) by computing a description of + /// the initial state in a , and then for each such state description we decide what the test + /// or evaluation will be at that state, and compute the successor state descriptions. A state description + /// represented by a is a collection of partially matched cases represented by . When we have computed descriptions for all of the states, we + /// create a new for each of them, containing the state transitions (including + /// the test to perform at each node and the successor nodes) but not the state descriptions. A containing this set of nodes becomes part of the bound nodes (e.g. in and ) and is used for semantic + /// analysis and lowering. /// /// internal sealed partial class DecisionDagBuilder { + private static readonly ObjectPool> s_uniqueStatePool = + PooledDictionary.CreatePool(DagStateEquivalence.Instance); + private readonly CSharpCompilation _compilation; private readonly Conversions _conversions; private readonly BindingDiagnosticBag _diagnostics; @@ -132,7 +131,11 @@ private BoundDecisionDag CreateDecisionDagForIsPattern( LabelSymbol whenTrueLabel) { var rootIdentifier = BoundDagTemp.ForOriginalInput(inputExpression); - return MakeBoundDecisionDag(syntax, ImmutableArray.Create(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, whenTrueLabel))); + + using var builder = TemporaryArray.Empty; + builder.Add(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, whenTrueLabel)); + + return MakeBoundDecisionDag(syntax, ref builder.AsRef()); } private BoundDecisionDag CreateDecisionDagForSwitchStatement( @@ -142,7 +145,7 @@ private BoundDecisionDag CreateDecisionDagForSwitchStatement( { var rootIdentifier = BoundDagTemp.ForOriginalInput(switchGoverningExpression); int i = 0; - var builder = ArrayBuilder.GetInstance(switchSections.Length); + using var builder = TemporaryArray.GetInstance(switchSections.Length); foreach (BoundSwitchSection section in switchSections) { foreach (BoundSwitchLabel label in section.SwitchLabels) @@ -154,7 +157,7 @@ private BoundDecisionDag CreateDecisionDagForSwitchStatement( } } - return MakeBoundDecisionDag(syntax, builder.ToImmutableAndFree()); + return MakeBoundDecisionDag(syntax, ref builder.AsRef()); } /// @@ -167,11 +170,11 @@ private BoundDecisionDag CreateDecisionDagForSwitchExpression( { var rootIdentifier = BoundDagTemp.ForOriginalInput(switchExpressionInput); int i = 0; - var builder = ArrayBuilder.GetInstance(switchArms.Length); + using var builder = TemporaryArray.GetInstance(switchArms.Length); foreach (BoundSwitchExpressionArm arm in switchArms) builder.Add(MakeTestsForPattern(++i, arm.Syntax, rootIdentifier, arm.Pattern, arm.WhenClause, arm.Label)); - return MakeBoundDecisionDag(syntax, builder.ToImmutableAndFree()); + return MakeBoundDecisionDag(syntax, ref builder.AsRef()); } /// @@ -704,10 +707,13 @@ private TypeSymbol ErrorType(string name = "") /// decision when no decision appears to match. This implementation is nonrecursive to avoid /// overflowing the compiler's evaluation stack when compiling a large switch statement. /// - private BoundDecisionDag MakeBoundDecisionDag(SyntaxNode syntax, ImmutableArray cases) + private BoundDecisionDag MakeBoundDecisionDag(SyntaxNode syntax, ref TemporaryArray cases) { + // A mapping used to make each DagState unique (i.e. to de-dup identical states). + PooledDictionary uniqueState = s_uniqueStatePool.Allocate(); + // Build the state machine underlying the decision dag - DecisionDag decisionDag = MakeDecisionDag(cases); + DecisionDag decisionDag = MakeDecisionDag(ref cases, uniqueState); // Note: It is useful for debugging the dag state table construction to set a breakpoint // here and view `decisionDag.Dump()`. @@ -721,6 +727,16 @@ private BoundDecisionDag MakeBoundDecisionDag(SyntaxNode syntax, ImmutableArray< var rootDecisionDagNode = decisionDag.RootNode.Dag; RoslynDebug.Assert(rootDecisionDagNode != null); var boundDecisionDag = new BoundDecisionDag(rootDecisionDagNode.Syntax, rootDecisionDagNode); + + // Now go and clean up all the dag states we created + foreach (var kvp in uniqueState) + { + Debug.Assert(kvp.Key == kvp.Value); + kvp.Key.ClearAndFree(); + } + + uniqueState.Free(); + #if DEBUG // Note that this uses the custom equality in `BoundDagEvaluation` // to make "equivalent" evaluation nodes share the same ID. @@ -767,13 +783,14 @@ int tempIdentifier(BoundDagEvaluation e) /// Make a (state machine) starting with the given set of cases in the root node, /// and return the node for the root. /// - private DecisionDag MakeDecisionDag(ImmutableArray casesForRootNode) + private DecisionDag MakeDecisionDag( + ref TemporaryArray casesForRootNode, + Dictionary uniqueState) { - // A work list of DagStates whose successors need to be computed - var workList = ArrayBuilder.GetInstance(); - - // A mapping used to make each DagState unique (i.e. to de-dup identical states). - var uniqueState = new Dictionary(DagStateEquivalence.Instance); + // A work list of DagStates whose successors need to be computed. In practice (measured in roslyn and in + // tests, >75% of the time this worklist never goes past 4 items, so a TemporaryArray is a good choice here + // to keep everything on the stack. + using var workList = TemporaryArray.Empty; // We "intern" the states, so that we only have a single object representing one // semantic state. Because the decision automaton may contain states that have more than one @@ -781,13 +798,19 @@ private DecisionDag MakeDecisionDag(ImmutableArray casesForRootNod // so that it is processed only once. This object identity uniqueness will be important later when we // start mutating the DagState nodes to compute successors and BoundDecisionDagNodes // for each one. That is why we have to use an equivalence relation in the dictionary `uniqueState`. - DagState uniqifyState(ImmutableArray cases, ImmutableDictionary remainingValues) + DagState uniquifyState(FrozenArrayBuilder cases, ImmutableDictionary remainingValues) { - var state = new DagState(cases, remainingValues); + var state = DagState.GetInstance(cases, remainingValues); if (uniqueState.TryGetValue(state, out DagState? existingState)) { - // We found an existing state that matches. Update its set of possible remaining values - // of each temp by taking the union of the sets on each incoming edge. + // We found an existing state that matches. Return the state we just created back to the pool and + // use the existing one instead. Null out the 'state' local so that any attempts to use it will + // fail fast. + state.ClearAndFree(); + state = null; + + // Update its set of possible remaining values of each temp by taking the union of the sets on each + // incoming edge. var newRemainingValues = ImmutableDictionary.CreateBuilder(); foreach (var (dagTemp, valuesForTemp) in remainingValues) { @@ -805,7 +828,7 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary cases, ImmutableDictionary.GetInstance(casesForRootNode.Length); + var rewrittenCases = ArrayBuilder.GetInstance(casesForRootNode.Count); foreach (var state in casesForRootNode) { var rewrittenCase = state.RewriteNestedLengthTests(); @@ -832,17 +855,19 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary.Empty); + var initialState = uniquifyState(new + FrozenArrayBuilder(rewrittenCases), + ImmutableDictionary.Empty); // Go through the worklist of DagState nodes for which we have not yet computed // successor states. while (workList.Count != 0) { - DagState state = workList.Pop(); + DagState state = workList.RemoveLast(); RoslynDebug.Assert(state.SelectedTest == null); RoslynDebug.Assert(state.TrueBranch == null); RoslynDebug.Assert(state.FalseBranch == null); - if (state.Cases.IsDefaultOrEmpty) + if (state.Cases.Count == 0) { // If this state has no more cases that could possibly match, then // we know there is no case that will match and this node represents a "default" @@ -867,7 +892,7 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary cases, ImmutableDictionary whenTrueDecisions, - out ImmutableArray whenFalseDecisions, - out ImmutableDictionary whenTrueValues, - out ImmutableDictionary whenFalseValues, + out var whenTrueDecisions, out var whenTrueValues, + out var whenFalseDecisions, out var whenFalseValues, ref foundExplicitNullTest); - state.TrueBranch = uniqifyState(whenTrueDecisions, whenTrueValues); - state.FalseBranch = uniqifyState(whenFalseDecisions, whenFalseValues); + state.TrueBranch = uniquifyState(whenTrueDecisions, whenTrueValues); + state.FalseBranch = uniquifyState(whenFalseDecisions, whenFalseValues); if (foundExplicitNullTest && d is BoundDagNonNullTest { IsExplicitTest: false } t) { // Turn an "implicit" non-null test into an explicit one @@ -914,7 +937,6 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary= 0; i--) { var state = sortedStates[i]; - if (state.Cases.IsDefaultOrEmpty) + if (state.Cases.Count == 0) { state.Dag = defaultDecision; continue; @@ -1035,15 +1057,15 @@ private void SplitCase( private void SplitCases( DagState state, BoundDagTest test, - out ImmutableArray whenTrue, - out ImmutableArray whenFalse, + out FrozenArrayBuilder whenTrue, out ImmutableDictionary whenTrueValues, + out FrozenArrayBuilder whenFalse, out ImmutableDictionary whenFalseValues, ref bool foundExplicitNullTest) { - ImmutableArray cases = state.Cases; - var whenTrueBuilder = ArrayBuilder.GetInstance(cases.Length); - var whenFalseBuilder = ArrayBuilder.GetInstance(cases.Length); + var cases = state.Cases; + var whenTrueBuilder = ArrayBuilder.GetInstance(cases.Count); + var whenFalseBuilder = ArrayBuilder.GetInstance(cases.Count); (whenTrueValues, whenFalseValues, bool whenTruePossible, bool whenFalsePossible) = SplitValues(state.RemainingValues, test); // whenTruePossible means the test could possibly have succeeded. whenFalsePossible means it could possibly have failed. // Tests that are either impossible or tautological (i.e. either of these false) given @@ -1068,8 +1090,8 @@ private void SplitCases( whenFalseBuilder.Add(whenFalseState); } - whenTrue = whenTrueBuilder.ToImmutableAndFree(); - whenFalse = whenFalseBuilder.ToImmutableAndFree(); + whenTrue = AsFrozen(whenTrueBuilder); + whenFalse = AsFrozen(whenFalseBuilder); } private static ( @@ -1154,9 +1176,9 @@ private static (BoundDagTemp input, BoundDagTemp lengthTemp, int index) GetCanon return (OriginalInput(input), lengthTemp, index); } - private static ImmutableArray RemoveEvaluation(ImmutableArray cases, BoundDagEvaluation e) + private static FrozenArrayBuilder RemoveEvaluation(FrozenArrayBuilder cases, BoundDagEvaluation e) { - var builder = ArrayBuilder.GetInstance(cases.Length); + var builder = ArrayBuilder.GetInstance(cases.Count); foreach (var stateForCase in cases) { var remainingTests = stateForCase.RemainingTests.RemoveEvaluation(e); @@ -1174,7 +1196,7 @@ private static ImmutableArray RemoveEvaluation(ImmutableArray @@ -1575,24 +1597,10 @@ public DecisionDag(DagState rootNode) /// /// A successor function used to topologically sort the DagState set. /// - private static ImmutableArray Successor(DagState state) + private static void AddSuccessor(ref TemporaryArray builder, DagState state) { - if (state.TrueBranch != null && state.FalseBranch != null) - { - return ImmutableArray.Create(state.FalseBranch, state.TrueBranch); - } - else if (state.TrueBranch != null) - { - return ImmutableArray.Create(state.TrueBranch); - } - else if (state.FalseBranch != null) - { - return ImmutableArray.Create(state.FalseBranch); - } - else - { - return ImmutableArray.Empty; - } + builder.AddIfNotNull(state.TrueBranch); + builder.AddIfNotNull(state.FalseBranch); } /// @@ -1602,7 +1610,7 @@ private static ImmutableArray Successor(DagState state) /// True if the graph was acyclic. public bool TryGetTopologicallySortedReachableStates(out ImmutableArray result) { - return TopologicalSort.TryIterativeSort(SpecializedCollections.SingletonEnumerable(this.RootNode), Successor, out result); + return TopologicalSort.TryIterativeSort(this.RootNode, AddSuccessor, out result); } #if DEBUG @@ -1642,7 +1650,7 @@ string tempName(BoundDagTemp t) foreach (DagState state in allStates) { - bool isFail = state.Cases.IsEmpty; + bool isFail = state.Cases.Count == 0; bool starred = isFail || state.Cases.First().PatternIsSatisfied; result.Append($"{(starred ? "*" : "")}State " + stateIdentifierMap[state] + (isFail ? " FAIL" : "")); var remainingValues = state.RemainingValues.Select(kvp => $"{tempName(kvp.Key)}:{kvp.Value}"); @@ -1732,6 +1740,55 @@ string dumpDagTest(BoundDagTest d) #endif } + private static FrozenArrayBuilder AsFrozen(ArrayBuilder builder) + => new FrozenArrayBuilder(builder); + + /// + /// This is a readonly wrapper around an array builder. It ensures we can benefit from the pooling an array builder provides, without having to incur + /// intermediary allocations for s. + /// + private readonly struct FrozenArrayBuilder + { + private readonly ArrayBuilder _arrayBuilder; + + public FrozenArrayBuilder(ArrayBuilder arrayBuilder) + { + Debug.Assert(arrayBuilder != null); + _arrayBuilder = arrayBuilder; + } + +#if DEBUG + + public bool IsDefault + => _arrayBuilder is null; + +#endif + + public void Free() + => _arrayBuilder.Free(); + + public int Count => _arrayBuilder.Count; + + public T this[int i] => _arrayBuilder[i]; + + public T First() => _arrayBuilder.First(); + + public ArrayBuilder.Enumerator GetEnumerator() => _arrayBuilder.GetEnumerator(); + + public FrozenArrayBuilder RemoveAt(int index) + { + var builder = ArrayBuilder.GetInstance(this.Count - 1); + + for (int i = 0; i < index; i++) + builder.Add(this[i]); + + for (int i = index + 1, n = this.Count; i < n; i++) + builder.Add(this[i]); + + return AsFrozen(builder); + } + } + /// /// The state at a given node of the decision finite state automaton. This is used during computation of the state /// machine (), and contains a representation of the meaning of the state. Because we always make @@ -1740,6 +1797,8 @@ string dumpDagTest(BoundDagTest d) /// private sealed class DagState { + private static readonly ObjectPool s_dagStatePool = new ObjectPool(static () => new DagState()); + /// /// For each dag temp of a type for which we track such things (the integral types, floating-point types, and bool), /// the possible values it can take on when control reaches this state. @@ -1748,18 +1807,12 @@ private sealed class DagState /// as the set of possible values can affect successor states. /// A absent from this dictionary means that all values of the type are possible. /// - public ImmutableDictionary RemainingValues { get; private set; } + public ImmutableDictionary RemainingValues { get; private set; } = null!; /// /// The set of cases that may still match, and for each of them the set of tests that remain to be tested. /// - public readonly ImmutableArray Cases; - - public DagState(ImmutableArray cases, ImmutableDictionary remainingValues) - { - this.Cases = cases; - this.RemainingValues = remainingValues; - } + public FrozenArrayBuilder Cases { get; private set; } // If not a leaf node or a when clause, the test that will be taken at this node of the // decision automaton. @@ -1774,6 +1827,49 @@ public DagState(ImmutableArray cases, ImmutableDictionary + /// Created an instance of . Will take ownership of . That + /// will be returned to its pool when is + /// called on this. + /// + public static DagState GetInstance(FrozenArrayBuilder cases, ImmutableDictionary remainingValues) + { + var dagState = s_dagStatePool.Allocate(); + +#if DEBUG + + Debug.Assert(dagState.Cases.IsDefault); + Debug.Assert(dagState.RemainingValues is null); + Debug.Assert(dagState.SelectedTest is null); + Debug.Assert(dagState.TrueBranch is null); + Debug.Assert(dagState.FalseBranch is null); + Debug.Assert(dagState.Dag is null); + +#endif + + dagState.Cases = cases; + dagState.RemainingValues = remainingValues; + + return dagState; + } + + public void ClearAndFree() + { + Cases.Free(); + Cases = default; + RemainingValues = null!; + SelectedTest = null; + TrueBranch = null; + FalseBranch = null; + Dag = null; + + s_dagStatePool.Free(this); + } + /// /// Decide on what test to use at this node of the decision dag. This is the principal /// heuristic we can change to adjust the quality of the generated decision automaton. @@ -1808,12 +1904,28 @@ public bool Equals(DagState? x, DagState? y) { RoslynDebug.Assert(x is { }); RoslynDebug.Assert(y is { }); - return x == y || x.Cases.SequenceEqual(y.Cases, (a, b) => a.Equals(b)); + if (x == y) + return true; + + if (x.Cases.Count != y.Cases.Count) + return false; + + for (int i = 0, n = x.Cases.Count; i < n; i++) + { + if (!x.Cases[i].Equals(y.Cases[i])) + return false; + } + + return true; } public int GetHashCode(DagState x) { - return Hash.Combine(Hash.CombineValues(x.Cases), x.Cases.Length); + var hashCode = 0; + foreach (var value in x.Cases) + hashCode = Hash.Combine(value.GetHashCode(), hashCode); + + return Hash.Combine(hashCode, x.Cases.Count); } } @@ -1821,7 +1933,7 @@ public int GetHashCode(DagState x) /// As part of the description of a node of the decision automaton, we keep track of what tests /// remain to be done for each case. /// - private sealed class StateForCase + private readonly struct StateForCase { /// /// A number that is distinct for each case and monotonically increasing from earlier to later cases. @@ -1875,9 +1987,7 @@ public bool Equals(StateForCase other) { // We do not include Syntax, Bindings, WhereClause, or CaseLabel // because once the Index is the same, those must be the same too. - return this == other || - other != null && - this.Index == other.Index && + return this.Index == other.Index && this.RemainingTests.Equals(other.RemainingTests); } diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index ef254301c1296..6c22b41537c6f 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -432,7 +432,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno var foreachKeyword = _syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, getEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, getEnumeratorMethod, foreachKeyword.GetLocation(), isDelegateConversion: false); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, getEnumeratorMethod, foreachKeyword, isDelegateConversion: false); // MoveNext is an instance method, so it does not need to have unmanaged callers only diagnostics reported. // Either a diagnostic was reported at the declaration of the method (for the invalid attribute), or MoveNext // is marked as not supported and we won't get here in the first place (for metadata import). diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index 1592f2bac88c3..6f62adbf611d9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -25,6 +25,13 @@ namespace Microsoft.CodeAnalysis.CSharp // in uncommon cases an instance of this class is attached to the conversion. private class UncommonData { + public static readonly UncommonData NoApplicableOperators = new UncommonData( + isExtensionMethod: false, + isArrayIndex: false, + conversionResult: UserDefinedConversionResult.NoApplicableOperators(ImmutableArray.Empty), + conversionMethod: null, + nestedConversions: default); + public UncommonData( bool isExtensionMethod, bool isArrayIndex, @@ -107,12 +114,14 @@ internal Conversion(UserDefinedConversionResult conversionResult, bool isImplici ? ConversionKind.NoConversion : isImplicit ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; - _uncommonData = new UncommonData( - isExtensionMethod: false, - isArrayIndex: false, - conversionResult: conversionResult, - conversionMethod: null, - nestedConversions: default); + _uncommonData = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators && conversionResult.Results.IsEmpty + ? UncommonData.NoApplicableOperators + : new UncommonData( + isExtensionMethod: false, + isArrayIndex: false, + conversionResult: conversionResult, + conversionMethod: null, + nestedConversions: default); } // For the method group, lambda and anonymous method conversions diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index b8599bddf7e3f..e5aba45435135 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -475,9 +475,8 @@ private void GetEnumOperation(BinaryOperatorKind kind, TypeSymbol enumType, Boun Debug.Assert((object)underlying != null); Debug.Assert(underlying.SpecialType != SpecialType.None); - var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T); - var nullableEnum = nullable.Construct(enumType); - var nullableUnderlying = nullable.Construct(underlying); + var nullableEnum = MakeNullable(enumType); + var nullableUnderlying = MakeNullable(underlying); switch (kind) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index f849ec6edd41b..4c6c2656b402f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -17,7 +17,7 @@ internal sealed partial class OverloadResolution { private NamedTypeSymbol MakeNullable(TypeSymbol type) { - return Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); + return Compilation.GetOrCreateNullableType(type); } public void UnaryOperatorOverloadResolution(UnaryOperatorKind kind, bool isChecked, BoundExpression operand, UnaryOperatorOverloadResolutionResult result, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs index dc60e8168ab82..4d3b4e61495a9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs @@ -1033,7 +1033,10 @@ private bool HadConstructedParameterFailedConstraintCheck( // formal parameter type. TypeSymbol formalParameterType = method.GetParameterType(result.Result.BadParameter); - formalParameterType.CheckAllConstraints(new ConstraintsHelper.CheckConstraintsArgsBoxed((CSharpCompilation)compilation, conversions, includeNullability: false, location, diagnostics)); + + var boxedArgs = ConstraintsHelper.CheckConstraintsArgsBoxed.Allocate(compilation, conversions, includeNullability: false, location, diagnostics); + formalParameterType.CheckAllConstraints(boxedArgs); + boxedArgs.Free(); return true; } diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs index a70f367e3446b..3dcf824ff86db 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs @@ -267,7 +267,7 @@ private BoundSwitchLabel BindSwitchSectionLabel( { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; - MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node, node.Keyword.GetLocation()); + MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node.Keyword); BoundPattern pattern = sectionBinder.BindPattern( matchLabelSyntax.Pattern, SwitchGoverningType, permitDesignations: true, node.HasErrors, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs index 463109a589e10..73ff46d298ef9 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -84,7 +85,7 @@ private bool CheckSwitchExpressionExhaustive( // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. - bool wasAcyclic = TopologicalSort.TryIterativeSort(SpecializedCollections.SingletonEnumerable(decisionDag.RootNode), nonNullSuccessors, out var nodes); + bool wasAcyclic = TopologicalSort.TryIterativeSort(decisionDag.RootNode, addNonNullSuccessors, out var nodes); // Since decisionDag.RootNode is acyclic by construction, its subset of nodes sorted here cannot be cyclic Debug.Assert(wasAcyclic); foreach (var n in nodes) @@ -107,7 +108,7 @@ private bool CheckSwitchExpressionExhaustive( return false; - ImmutableArray nonNullSuccessors(BoundDecisionDagNode n) + static void addNonNullSuccessors(ref TemporaryArray builder, BoundDecisionDagNode n) { switch (n) { @@ -115,14 +116,18 @@ ImmutableArray nonNullSuccessors(BoundDecisionDagNode n) switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null - return ImmutableArray.Create(p.WhenTrue); + builder.Add(p.WhenTrue); + return; case BoundDagExplicitNullTest t: // checks that the input is null - return ImmutableArray.Create(p.WhenFalse); + builder.Add(p.WhenFalse); + return; default: - return BoundDecisionDag.Successors(n); + BoundDecisionDag.AddSuccessors(ref builder, n); + return; } default: - return BoundDecisionDag.Successors(n); + BoundDecisionDag.AddSuccessors(ref builder, n); + return; } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs index eec713ae6fbbc..1deac891fe1d9 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs @@ -11,6 +11,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -20,18 +21,23 @@ internal partial class BoundDecisionDag private ImmutableHashSet _reachableLabels; private ImmutableArray _topologicallySortedNodes; - internal static ImmutableArray Successors(BoundDecisionDagNode node) + internal static void AddSuccessors(ref TemporaryArray builder, BoundDecisionDagNode node) { switch (node) { case BoundEvaluationDecisionDagNode p: - return ImmutableArray.Create(p.Next); + builder.Add(p.Next); + return; case BoundTestDecisionDagNode p: - return ImmutableArray.Create(p.WhenFalse, p.WhenTrue); + builder.Add(p.WhenFalse); + builder.Add(p.WhenTrue); + return; case BoundLeafDecisionDagNode d: - return ImmutableArray.Empty; + return; case BoundWhenDecisionDagNode w: - return (w.WhenFalse != null) ? ImmutableArray.Create(w.WhenTrue, w.WhenFalse) : ImmutableArray.Create(w.WhenTrue); + builder.Add(w.WhenTrue); + builder.AddIfNotNull(w.WhenFalse); + return; default: throw ExceptionUtilities.UnexpectedValue(node.Kind); } @@ -69,7 +75,7 @@ public ImmutableArray TopologicallySortedNodes if (_topologicallySortedNodes.IsDefault) { // We use an iterative topological sort to avoid overflowing the compiler's runtime stack for a large switch statement. - bool wasAcyclic = TopologicalSort.TryIterativeSort(SpecializedCollections.SingletonEnumerable(this.RootNode), Successors, out _topologicallySortedNodes); + bool wasAcyclic = TopologicalSort.TryIterativeSort(this.RootNode, AddSuccessors, out _topologicallySortedNodes); // Since these nodes were constructed by an isomorphic mapping from a known acyclic graph, it cannot be cyclic Debug.Assert(wasAcyclic); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 7b3d76e2f2759..d0db9d09556ab 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6814,6 +6814,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Parameter is unread. Did you forget to use it to initialize the property with that name? + + instance member in 'nameof' + The primary constructor conflicts with the synthesized copy constructor. diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index b34e3fa65ab71..a1ed8fb58df5d 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -202,7 +202,7 @@ private void EmitExpressionCore(BoundExpression expression, bool used) break; case BoundKind.IsOperator: - EmitIsExpression((BoundIsOperator)expression, used); + EmitIsExpression((BoundIsOperator)expression, used, omitBooleanConversion: false); break; case BoundKind.AsOperator: @@ -3158,7 +3158,7 @@ private void EmitPopIfUnused(bool used) } } - private void EmitIsExpression(BoundIsOperator isOp, bool used) + private void EmitIsExpression(BoundIsOperator isOp, bool used, bool omitBooleanConversion) { var operand = isOp.Operand; EmitExpression(operand, used); @@ -3172,8 +3172,12 @@ private void EmitIsExpression(BoundIsOperator isOp, bool used) } _builder.EmitOpCode(ILOpCode.Isinst); EmitSymbolToken(isOp.TargetType.Type, isOp.Syntax); - _builder.EmitOpCode(ILOpCode.Ldnull); - _builder.EmitOpCode(ILOpCode.Cgt_un); + + if (!omitBooleanConversion) + { + _builder.EmitOpCode(ILOpCode.Ldnull); + _builder.EmitOpCode(ILOpCode.Cgt_un); + } } } @@ -3483,6 +3487,22 @@ private void EmitConditionalOperator(BoundConditionalOperator expr, bool used) { Debug.Assert(expr.ConstantValueOpt == null, "Constant value should have been emitted directly"); + // Generate branchless IL for (b ? 1 : 0). + if (used && _ilEmitStyle != ILEmitStyle.Debug && + (IsNumeric(expr.Type) || expr.Type.PrimitiveTypeCode == Cci.PrimitiveTypeCode.Boolean) && + hasIntegralValueZeroOrOne(expr.Consequence, out var isConsequenceOne) && + hasIntegralValueZeroOrOne(expr.Alternative, out var isAlternativeOne) && + isConsequenceOne != isAlternativeOne && + TryEmitComparison(expr.Condition, sense: isConsequenceOne)) + { + var toType = expr.Type.PrimitiveTypeCode; + if (toType != Cci.PrimitiveTypeCode.Boolean) + { + _builder.EmitNumericConversion(Cci.PrimitiveTypeCode.Int32, toType, @checked: false); + } + return; + } + object consequenceLabel = new object(); object doneLabel = new object(); @@ -3547,6 +3567,33 @@ private void EmitConditionalOperator(BoundConditionalOperator expr, bool used) } _builder.MarkLabel(doneLabel); + + static bool hasIntegralValueZeroOrOne(BoundExpression expr, out bool isOne) + { + if (expr.ConstantValueOpt is { } constantValue) + { + if (constantValue is { IsIntegral: true, UInt64Value: (1 or 0) and var i }) + { + isOne = i == 1; + return true; + } + + if (constantValue is { IsBoolean: true, BooleanValue: var b }) + { + isOne = b; + return true; + } + + if (constantValue is { IsChar: true, CharValue: ((char)1 or (char)0) and var c }) + { + isOne = c == 1; + return true; + } + } + + isOne = false; + return false; + } } /// diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitOperators.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitOperators.cs index f9f22aa26413d..80ca057d83076 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitOperators.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitOperators.cs @@ -471,13 +471,7 @@ private void EmitBinaryCondOperatorHelper(ILOpCode opCode, BoundExpression left, // this will leave a value on the stack which conforms to sense, ie:(condition == sense) private void EmitCondExpr(BoundExpression condition, bool sense) { - while (condition.Kind == BoundKind.UnaryOperator) - { - var unOp = (BoundUnaryOperator)condition; - Debug.Assert(unOp.OperatorKind == UnaryOperatorKind.BoolLogicalNegation); - condition = unOp.Operand; - sense = !sense; - } + RemoveNegation(ref condition, ref sense); Debug.Assert(condition.Type.SpecialType == SpecialType.System_Boolean); @@ -506,6 +500,64 @@ private void EmitCondExpr(BoundExpression condition, bool sense) return; } + /// + /// Emits boolean expression without branching if possible (i.e., no logical operators, only comparisons). + /// Leaves a boolean (int32, 0 or 1) value on the stack which conforms to sense, i.e., condition == sense. + /// + private bool TryEmitComparison(BoundExpression condition, bool sense) + { + RemoveNegation(ref condition, ref sense); + + Debug.Assert(condition.Type.SpecialType == SpecialType.System_Boolean); + + if (condition.ConstantValueOpt is { } constantValue) + { + Debug.Assert(constantValue.Discriminator == ConstantValueTypeDiscriminator.Boolean); + _builder.EmitBoolConstant(constantValue.BooleanValue == sense); + return true; + } + + if (condition is BoundBinaryOperator binOp) + { + // Intentionally don't optimize logical operators, they need branches to short-circuit. + if (binOp.OperatorKind.IsComparison()) + { + EmitBinaryCondOperator(binOp, sense: sense); + return true; + } + } + else if (condition is BoundIsOperator isOp) + { + EmitIsExpression(isOp, used: true, omitBooleanConversion: true); + + // Convert to 1 or 0. + _builder.EmitOpCode(ILOpCode.Ldnull); + _builder.EmitOpCode(sense ? ILOpCode.Cgt_un : ILOpCode.Ceq); + return true; + } + else + { + EmitExpression(condition, used: true); + + // Convert to 1 or 0 (although `condition` is of type `bool`, it can contain any integer). + _builder.EmitOpCode(ILOpCode.Ldc_i4_0); + _builder.EmitOpCode(sense ? ILOpCode.Cgt_un : ILOpCode.Ceq); + return true; + } + + return false; + } + + private static void RemoveNegation(ref BoundExpression condition, ref bool sense) + { + while (condition is BoundUnaryOperator unOp) + { + Debug.Assert(unOp.OperatorKind == UnaryOperatorKind.BoolLogicalNegation); + condition = unOp.Operand; + sense = !sense; + } + } + private void EmitUnaryCheckedOperatorExpression(BoundUnaryOperator expression, bool used) { Debug.Assert(expression.OperatorKind.Operator() == UnaryOperatorKind.UnaryMinus); diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index 03905c87e77ac..5d20d4fa4a07b 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -2300,6 +2300,8 @@ internal override SyntaxNode GetDeclaratorSyntax() throw new NotImplementedException(); } + internal override bool HasSourceLocation => false; + public override RefKind RefKind { get { return RefKind.None; } diff --git a/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs b/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs index 8b4d330e83a70..0cc6a7bffb1f9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs +++ b/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs @@ -281,7 +281,7 @@ internal UnaryOperatorSignature GetSignature(UnaryOperatorKind kind) if (kind.IsLifted()) { - opType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(opType); + opType = _compilation.GetOrCreateNullableType(opType); } return new UnaryOperatorSignature(kind, opType, opType); @@ -723,7 +723,7 @@ internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind) TypeSymbol rightType = _compilation.GetSpecialType(SpecialType.System_Int32); if (kind.IsLifted()) { - rightType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(rightType); + rightType = _compilation.GetOrCreateNullableType(rightType); } return new BinaryOperatorSignature(kind, left, rightType, left); @@ -837,23 +837,22 @@ private TypeSymbol LiftedType(BinaryOperatorKind kind) { Debug.Assert(kind.IsLifted()); - var nullable = _compilation.GetSpecialType(SpecialType.System_Nullable_T); - - switch (kind.OperandTypes()) + var typeArgument = kind.OperandTypes() switch { - case BinaryOperatorKind.Int: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int32)); - case BinaryOperatorKind.UInt: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt32)); - case BinaryOperatorKind.Long: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int64)); - case BinaryOperatorKind.ULong: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt64)); - case BinaryOperatorKind.NInt: return nullable.Construct(_compilation.CreateNativeIntegerTypeSymbol(signed: true)); - case BinaryOperatorKind.NUInt: return nullable.Construct(_compilation.CreateNativeIntegerTypeSymbol(signed: false)); - case BinaryOperatorKind.Float: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Single)); - case BinaryOperatorKind.Double: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Double)); - case BinaryOperatorKind.Decimal: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Decimal)); - case BinaryOperatorKind.Bool: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Boolean)); - } - Debug.Assert(false, "Bad operator kind in lifted type"); - return null; + BinaryOperatorKind.Int => _compilation.GetSpecialType(SpecialType.System_Int32), + BinaryOperatorKind.UInt => _compilation.GetSpecialType(SpecialType.System_UInt32), + BinaryOperatorKind.Long => _compilation.GetSpecialType(SpecialType.System_Int64), + BinaryOperatorKind.ULong => _compilation.GetSpecialType(SpecialType.System_UInt64), + BinaryOperatorKind.NInt => _compilation.CreateNativeIntegerTypeSymbol(signed: true), + BinaryOperatorKind.NUInt => _compilation.CreateNativeIntegerTypeSymbol(signed: false), + BinaryOperatorKind.Float => _compilation.GetSpecialType(SpecialType.System_Single), + BinaryOperatorKind.Double => _compilation.GetSpecialType(SpecialType.System_Double), + BinaryOperatorKind.Decimal => _compilation.GetSpecialType(SpecialType.System_Decimal), + BinaryOperatorKind.Bool => _compilation.GetSpecialType(SpecialType.System_Boolean), + var v => throw ExceptionUtilities.UnexpectedValue(v), + }; + + return _compilation.GetOrCreateNullableType(typeArgument); } internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, bool leftIsDefault, TypeSymbol rightType, bool rightIsNull, bool rightIsDefault, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 5f5c1a4c3d414..d7eeb560d938f 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -16,6 +16,7 @@ using Microsoft.Cci; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGen; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -1565,6 +1566,18 @@ internal AliasSymbol GlobalNamespaceAlias return result; } + /// + /// Cache of T to Nullable<T>. + /// + private ImmutableSegmentedDictionary _typeToNullableVersion = ImmutableSegmentedDictionary.Empty; + + internal NamedTypeSymbol GetOrCreateNullableType(TypeSymbol typeArgument) + => RoslynImmutableInterlocked.GetOrAdd( + ref _typeToNullableVersion, + typeArgument, + static (typeArgument, @this) => @this.GetSpecialType(SpecialType.System_Nullable_T).Construct(typeArgument), + this); + /// /// Get the symbol for the predefined type member from the COR Library referenced by this compilation. /// diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index e6651aa40f86e..f80216548811b 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1774,20 +1774,13 @@ private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan decla } } - foreach (var loc in symbol.Locations) + if (symbol.HasLocationContainedWithin(this.SyntaxTree, declarationSpan, out var wasZeroWidthMatch)) { - if (loc.IsInSource && loc.SourceTree == this.SyntaxTree && declarationSpan.Contains(loc.SourceSpan)) - { - if (loc.SourceSpan.IsEmpty && loc.SourceSpan.End == declarationSpan.Start) - { - // exclude decls created via syntax recovery - zeroWidthMatch = symbol; - } - else - { - return symbol; - } - } + if (!wasZeroWidthMatch) + return symbol; + + // exclude decls created via syntax recovery + zeroWidthMatch = symbol; } // Handle the case of the implementation of a partial method. diff --git a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs index 8608c0caaafe5..3e3cb7f5c9ffa 100644 --- a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs @@ -245,7 +245,7 @@ public bool Equals(TypeDeclarationIdentity other) return false; } - if ((object)thisDecl.Location.SourceTree != otherDecl.Location.SourceTree + if ((object)thisDecl.SyntaxReference.SyntaxTree != otherDecl.SyntaxReference.SyntaxTree && ((thisDecl.Modifiers & DeclarationModifiers.File) != 0 || (otherDecl.Modifiers & DeclarationModifiers.File) != 0)) { diff --git a/src/Compilers/CSharp/Portable/Errors/CSharpDiagnosticFormatter.cs b/src/Compilers/CSharp/Portable/Errors/CSharpDiagnosticFormatter.cs index 464ce1889ce43..e8ccce4fba389 100644 --- a/src/Compilers/CSharp/Portable/Errors/CSharpDiagnosticFormatter.cs +++ b/src/Compilers/CSharp/Portable/Errors/CSharpDiagnosticFormatter.cs @@ -11,5 +11,10 @@ internal CSharpDiagnosticFormatter() } public static new CSharpDiagnosticFormatter Instance { get; } = new CSharpDiagnosticFormatter(); + + internal override bool HasDefaultHelpLinkUri(Diagnostic diagnostic) + { + return diagnostic.Descriptor.HelpLinkUri == ErrorFacts.GetHelpLink((ErrorCode)diagnostic.Code); + } } } diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index e55e7ad573ffb..92e08d6ae0c10 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -265,6 +265,8 @@ internal enum MessageID IDS_FeaturePrimaryConstructors = MessageBase + 12833, IDS_FeatureUsingTypeAlias = MessageBase + 12834, + + IDS_FeatureInstanceMemberInNameof = MessageBase + 12835, } // Message IDs may refer to strings that need to be localized. @@ -314,31 +316,87 @@ public static LocalizableErrorArgument Localize(this MessageID id) } } + internal static bool CheckFeatureAvailability( + this MessageID feature, + DiagnosticBag diagnostics, + SyntaxNode syntax, + Location? location = null) + { + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree.Options, + static tuple => tuple.location ?? tuple.syntax.Location, + (syntax, location)); + } + + internal static bool CheckFeatureAvailability( + this MessageID feature, + DiagnosticBag diagnostics, + SyntaxToken syntax, + Location? location = null) + { + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree!.Options, + static tuple => tuple.location ?? tuple.syntax.GetLocation(), + (syntax, location)); + } + internal static bool CheckFeatureAvailability( this MessageID feature, BindingDiagnosticBag diagnostics, SyntaxNode syntax, Location? location = null) { - var diag = GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)syntax.SyntaxTree.Options); - if (diag is object) + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree.Options, + static tuple => tuple.location ?? tuple.syntax.Location, + (syntax, location)); + } + + internal static bool CheckFeatureAvailability( + this MessageID feature, + BindingDiagnosticBag diagnostics, + SyntaxToken syntax, + Location? location = null) + { + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree!.Options, + static tuple => tuple.location ?? tuple.syntax.GetLocation(), + (syntax, location)); + } + + private static bool CheckFeatureAvailability( + this MessageID feature, + DiagnosticBag diagnostics, + ParseOptions parseOptions, + Func getLocation, + TData data) + { + if (GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)parseOptions) is { } diagInfo) { - diagnostics.Add(diag, location ?? syntax.GetLocation()); + diagnostics.Add(diagInfo, getLocation(data)); return false; } return true; } - internal static bool CheckFeatureAvailability( + private static bool CheckFeatureAvailability( this MessageID feature, - DiagnosticBag diagnostics, - SyntaxNode syntax, - Location? location = null) + BindingDiagnosticBag diagnostics, + ParseOptions parseOptions, + Func getLocation, + TData data) { - var diag = GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)syntax.SyntaxTree.Options); - if (diag is object) + if (GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)parseOptions) is { } diagInfo) { - diagnostics.Add(diag, location ?? syntax.GetLocation()); + diagnostics.Add(diagInfo, getLocation(data)); return false; } return true; @@ -393,6 +451,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureLambdaParamsArray: // semantic check case MessageID.IDS_FeaturePrimaryConstructors: // declaration table check case MessageID.IDS_FeatureUsingTypeAlias: // semantic check + case MessageID.IDS_FeatureInstanceMemberInNameof: // semantic check return LanguageVersion.Preview; // C# 11.0 features. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index 3371188044f9a..f34b6e6c272a0 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -1170,7 +1170,7 @@ protected virtual void ReportUnassigned(Symbol symbol, SyntaxNode node, int slot if (skipIfUseBeforeDeclaration && symbol.Kind == SymbolKind.Local && - (symbol.Locations.Length == 0 || node.Span.End < symbol.GetFirstLocationOrNone().SourceSpan.Start)) + (symbol.TryGetFirstLocation() is var location && (location is null || node.Span.End < location.SourceSpan.Start))) { // We've already reported the use of a local before its declaration. No need to emit // another diagnostic for the same issue. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs index 75b434c276c6f..766175d46fb59 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.PlaceholderLocal.cs @@ -61,6 +61,7 @@ public override bool Equals(Symbol obj, TypeCompareKind compareKind) internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, BindingDiagnosticBag diagnostics = null) => null; internal override ImmutableBindingDiagnostic GetConstantValueDiagnostics(BoundExpression boundInitValue) => ImmutableBindingDiagnostic.Empty; internal override SyntaxNode GetDeclaratorSyntax() => throw ExceptionUtilities.Unreachable(); + internal override bool HasSourceLocation => false; internal override LocalSymbol WithSynthesizedLocalKindAndSyntax( SynthesizedLocalKind kind, SyntaxNode syntax #if DEBUG diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index f77b69e6d2038..2d3a913bbc1bd 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -2642,6 +2642,20 @@ private void InheritDefaultState(TypeSymbol targetType, int targetSlot) { Debug.Assert(targetSlot > 0); +#if DEBUG + var actualType = _variables[targetSlot].Symbol.GetTypeOrReturnType().Type; + Debug.Assert(actualType is { }); + + if (!actualType.ContainsErrorType() && + !targetType.ContainsErrorType()) + { + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + var conversionsWithoutNullability = _conversions.WithNullability(false); + var conversion = conversionsWithoutNullability.ClassifyImplicitConversionFromType(actualType, targetType, ref discardedUseSiteInfo); + Debug.Assert(conversion.Kind is ConversionKind.Identity or ConversionKind.ImplicitReference); + } +#endif + // Reset the state of any members of the target. var members = ArrayBuilder<(VariableIdentifier, int)>.GetInstance(); _variables.GetMembers(members, targetSlot); @@ -5105,12 +5119,27 @@ private static BoundExpression SkipReferenceConversions(BoundExpression possibly var leftState = this.State.Clone(); LearnFromNonNullTest(leftOperand, ref leftState); LearnFromNullTest(leftOperand, ref this.State); + + // If we are assigning to a nullable value type variable, set the top-level state of + // the LHS first, then change the slot to the Value property of the LHS to simulate + // assignment of the RHS and update the nullable state of the underlying value type. if (node.IsNullableValueTypeAssignment) { + Debug.Assert(targetType.Type.ContainsErrorType() || + node.Type?.ContainsErrorType() == true || + TypeSymbol.Equals(targetType.Type.GetNullableUnderlyingType(), node.Type, TypeCompareKind.AllIgnoreOptions)); + if (leftSlot > 0) + { + SetState(ref this.State, leftSlot, NullableFlowState.NotNull); + leftSlot = GetNullableOfTValueSlot(targetType.Type, leftSlot, out _); + } targetType = TypeWithAnnotations.Create(node.Type, NullableAnnotation.NotAnnotated); } + TypeWithState rightResult = VisitOptionalImplicitConversion(rightOperand, targetType, useLegacyWarnings: UseLegacyWarnings(leftOperand), trackMembers: false, AssignmentKind.Assignment); + Debug.Assert(TypeSymbol.Equals(targetType.Type, rightResult.Type, TypeCompareKind.AllIgnoreOptions)); TrackNullableStateForAssignment(rightOperand, targetType, leftSlot, rightResult, MakeSlot(rightOperand)); + Join(ref this.State, ref leftState); TypeWithState resultType = TypeWithState.Create(targetType.Type, rightResult.State); SetResultType(node, resultType); @@ -7920,7 +7949,7 @@ private TypeWithState VisitConversion( bool extensionMethodThisArgument = false, Optional stateForLambda = default, bool trackMembers = false, - Location? diagnosticLocationOpt = null, + Location? diagnosticLocation = null, ArrayBuilder? previousArgumentConversionResults = null) { Debug.Assert(!trackMembers || !IsConditionalState); @@ -7957,7 +7986,6 @@ private TypeWithState VisitConversion( NullableFlowState resultState = NullableFlowState.NotNull; bool canConvertNestedNullability = true; bool isSuppressed = false; - diagnosticLocationOpt ??= (conversionOpt ?? conversionOperand).Syntax.GetLocation(); if (conversionOperand.IsSuppressed == true) { @@ -7985,7 +8013,7 @@ private TypeWithState VisitConversion( } if (reportRemainingWarnings && invokeSignature != null) { - ReportNullabilityMismatchWithTargetDelegate(diagnosticLocationOpt, targetType, invokeSignature, method, conversion.IsExtensionMethod); + ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), targetType, invokeSignature, method, conversion.IsExtensionMethod); } } resultState = NullableFlowState.NotNull; @@ -8007,7 +8035,7 @@ private TypeWithState VisitConversion( VisitLambda(lambda, delegateType, stateForLambda); if (reportRemainingWarnings && delegateType is not null) { - ReportNullabilityMismatchWithTargetDelegate(diagnosticLocationOpt, delegateType, lambda); + ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), delegateType, lambda); } TrackAnalyzedNullabilityThroughConversionGroup(targetTypeWithNullability.ToTypeWithState(), conversionOpt, conversionOperand); @@ -8038,7 +8066,7 @@ private TypeWithState VisitConversion( case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: - return VisitUserDefinedConversion(conversionOpt, conversionOperand, conversion, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, diagnosticLocationOpt); + return VisitUserDefinedConversion(conversionOpt, conversionOperand, conversion, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, getDiagnosticLocation()); case ConversionKind.ExplicitDynamic: case ConversionKind.ImplicitDynamic: @@ -8054,7 +8082,7 @@ private TypeWithState VisitConversion( { if (!operandType.IsNotNull && reportRemainingWarnings) { - ReportDiagnostic(ErrorCode.WRN_UnboxPossibleNull, diagnosticLocationOpt); + ReportDiagnostic(ErrorCode.WRN_UnboxPossibleNull, getDiagnosticLocation()); } LearnFromNonNullTest(conversionOperand, ref State); @@ -8148,7 +8176,7 @@ private TypeWithState VisitConversion( // Explicit conversion of Nullable to T is equivalent to Nullable.Value. if (reportTopLevelWarnings && operandType.MayBeNull) { - ReportDiagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, diagnosticLocationOpt); + ReportDiagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, getDiagnosticLocation()); } // Mark the value as not nullable, regardless of whether it was known to be nullable, @@ -8214,17 +8242,17 @@ private TypeWithState VisitConversion( // Need to report all warnings that apply since the warnings can be suppressed individually. if (reportTopLevelWarnings) { - ReportNullableAssignmentIfNecessary(conversionOperand, targetTypeWithNullability, resultType, useLegacyWarnings, assignmentKind, parameterOpt, diagnosticLocationOpt); + ReportNullableAssignmentIfNecessary(conversionOperand, targetTypeWithNullability, resultType, useLegacyWarnings, assignmentKind, parameterOpt, getDiagnosticLocation()); } if (reportRemainingWarnings && !canConvertNestedNullability) { if (assignmentKind == AssignmentKind.Argument) { - ReportNullabilityMismatchInArgument(diagnosticLocationOpt, operandType.Type, parameterOpt, targetType, forOutput: false); + ReportNullabilityMismatchInArgument(getDiagnosticLocation(), operandType.Type, parameterOpt, targetType, forOutput: false); } else { - ReportNullabilityMismatchInAssignment(diagnosticLocationOpt, GetTypeAsDiagnosticArgument(operandType.Type), targetType); + ReportNullabilityMismatchInAssignment(getDiagnosticLocation(), GetTypeAsDiagnosticArgument(operandType.Type), targetType); } } } @@ -8233,6 +8261,13 @@ private TypeWithState VisitConversion( return resultType; + // Avoid realizing the diagnostic location until needed. + Location getDiagnosticLocation() + { + diagnosticLocation ??= (conversionOpt ?? conversionOperand).Syntax.GetLocation(); + return diagnosticLocation; + } + #nullable enable static TypeWithState calculateResultType(TypeWithAnnotations targetTypeWithNullability, bool fromExplicitCast, NullableFlowState resultState, bool isSuppressed, TypeSymbol targetType) { @@ -8475,7 +8510,7 @@ private TypeWithState VisitUserDefinedConversion( parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, - diagnosticLocationOpt: diagnosticLocation); + diagnosticLocation: diagnosticLocation); // Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605. // (see NullableReferenceTypesTests.ImplicitConversions_07). @@ -8688,7 +8723,7 @@ private TypeWithState ClassifyAndVisitConversion( parameterOpt, reportTopLevelWarnings: reportWarnings, reportRemainingWarnings: !fromExplicitCast && reportWarnings, - diagnosticLocationOpt: diagnosticLocation); + diagnosticLocation: diagnosticLocation); } public override BoundNode? VisitDelegateCreationExpression(BoundDelegateCreationExpression node) @@ -10221,7 +10256,7 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) AssignmentKind.ForEachIterationVariable, reportTopLevelWarnings: true, reportRemainingWarnings: true, - diagnosticLocationOpt: variableLocation); + diagnosticLocation: variableLocation); } // In non-error cases we'll only run this loop a single time. In error cases we'll set the nullability of the VariableType multiple times, but at least end up with something diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 55e82a0530129..0fd96f0aaf08d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -1558,9 +1558,8 @@ private BoundExpression CaptureExpressionInTempIfNeeded( private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value) { - NamedTypeSymbol nullableType = _compilation.GetSpecialType(SpecialType.System_Nullable_T); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); - NamedTypeSymbol nullableBoolType = nullableType.Construct(boolType); + NamedTypeSymbol nullableBoolType = _compilation.GetOrCreateNullableType(boolType); if (value == null) { return new BoundDefaultExpression(syntax, nullableBoolType); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index aefd2df3998c4..fb537a77a0ee1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -734,7 +734,7 @@ private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node if (binaryOperatorKind.IsLifted()) { - binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType); + binaryOperandType = _compilation.GetOrCreateNullableType(binaryOperandType); MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor); boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne); } diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 37cd8e76abcd6..a65297ed6621f 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -85,9 +85,10 @@ internal enum TerminatorState IsEndOfFunctionPointerParameterListErrored = 1 << 24, IsEndOfFunctionPointerCallingConvention = 1 << 25, IsEndOfRecordOrClassOrStructOrInterfaceSignature = 1 << 26, + IsExpressionOrPatternInCaseLabelOfSwitchStatement = 1 << 27, } - private const int LastTerminatorState = (int)TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature; + private const int LastTerminatorState = (int)TerminatorState.IsExpressionOrPatternInCaseLabelOfSwitchStatement; private bool IsTerminator() { @@ -6582,8 +6583,7 @@ when lastTokenOfType.Kind is not SyntaxKind.QuestionToken // don't allow `Type?? lastTokenOfType = this.EatToken(); result = ScanTypeFlags.NullableType; break; - case SyntaxKind.AsteriskToken - when lastTokenOfType.Kind != SyntaxKind.CloseBracketToken: // don't allow `Type[]*` + case SyntaxKind.AsteriskToken: // Check for pointer type(s) switch (mode) { @@ -6922,7 +6922,7 @@ bool canBeNullableType() return true; } - case SyntaxKind.AsteriskToken when type.Kind != SyntaxKind.ArrayType: + case SyntaxKind.AsteriskToken: switch (mode) { case ParseTypeMode.AfterIs: diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index e065a0f27b865..2fb24035996e9 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Roslyn.Utilities; @@ -50,6 +49,7 @@ when ConvertExpressionToType(expr, out var leftType): return false; }; } + private PatternSyntax ParsePattern(Precedence precedence, bool afterIs = false, bool whenIsKeyword = false) { return ParseDisjunctivePattern(precedence, afterIs, whenIsKeyword); @@ -429,7 +429,10 @@ private bool IsValidPatternDesignation(bool whenIsKeyword) private CSharpSyntaxNode ParseExpressionOrPatternForSwitchStatement() { + var savedState = _termState; + _termState |= TerminatorState.IsExpressionOrPatternInCaseLabelOfSwitchStatement; var pattern = ParsePattern(Precedence.Conditional, whenIsKeyword: true); + _termState = savedState; return ConvertPatternToExpressionIfPossible(pattern); } @@ -536,6 +539,13 @@ private static PostSkipAction SkipBadPatternListTokens( if (@this.CurrentToken.Kind is SyntaxKind.CloseParenToken or SyntaxKind.CloseBraceToken or SyntaxKind.CloseBracketToken or SyntaxKind.SemicolonToken) return PostSkipAction.Abort; + // `:` is usually treated as incorrect separation token. This helps for error recovery in basic typing scenarios like `{ Prop:$$ Prop1: { ... } }`. + // However, such behavior isn't much desirable when parsing pattern of a case label in a switch statement. For instance, consider the following example: `case { Prop: { }: case ...`. + // Normally we would skip second `:` and `case` keyword after it as bad tokens and continue parsing pattern, which produces a lot of noise errors. + // In order to avoid that and produce single error of missing `}` we exit on unexpected `:` in such cases. + if (@this._termState.HasFlag(TerminatorState.IsExpressionOrPatternInCaseLabelOfSwitchStatement) && @this.CurrentToken.Kind is SyntaxKind.ColonToken) + return PostSkipAction.Abort; + return @this.SkipBadSeparatedListTokensWithExpectedKind(ref open, list, static p => p.CurrentToken.Kind != SyntaxKind.CommaToken && !p.IsPossibleSubpatternElement(), static (p, closeKind) => p.CurrentToken.Kind == closeKind || p.CurrentToken.Kind == SyntaxKind.SemicolonToken, diff --git a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs index 487971e6bbae6..f39280f32a662 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs @@ -374,7 +374,7 @@ private NamespaceOrTypeSymbol ResolveAliasTarget( { if (usingDirective.UnsafeKeyword != default) { - MessageID.IDS_FeatureUsingTypeAlias.CheckFeatureAvailability(diagnostics, usingDirective, usingDirective.UnsafeKeyword.GetLocation()); + MessageID.IDS_FeatureUsingTypeAlias.CheckFeatureAvailability(diagnostics, usingDirective.UnsafeKeyword); } else if (usingDirective.NamespaceOrType is not NameSyntax) { diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs b/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs index fb5e870c96ebc..090acf51b4863 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs @@ -34,7 +34,7 @@ public static ConstantValue EvaluateFieldConstant( BindingDiagnosticBag diagnostics) { var compilation = symbol.DeclaringCompilation; - var binderFactory = compilation.GetBinderFactory((SyntaxTree)symbol.GetFirstLocation().SourceTree); + var binderFactory = compilation.GetBinderFactory(equalsValueNode.SyntaxTree); var binder = binderFactory.GetBinder(equalsValueNode); binder = new WithPrimaryConstructorParametersBinder(symbol.ContainingType, binder); diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs index 32a48fd9b36d1..7440e4a2216f0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstraintsHelper.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; using System.Runtime.CompilerServices; +using System.Threading; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -464,7 +465,9 @@ public static void CheckAllConstraints( BindingDiagnosticBag diagnostics) { bool includeNullability = compilation.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes); - type.CheckAllConstraints(new CheckConstraintsArgsBoxed(compilation, conversions, includeNullability, location, diagnostics)); + var boxedArgs = CheckConstraintsArgsBoxed.Allocate(compilation, conversions, includeNullability, location, diagnostics); + type.CheckAllConstraints(boxedArgs); + boxedArgs.Free(); } public static bool CheckAllConstraints( @@ -476,8 +479,10 @@ public static bool CheckAllConstraints( // Nullability checks can only add warnings here so skip them for this check as we are only // concerned with errors. - type.CheckAllConstraints(new CheckConstraintsArgsBoxed(compilation, conversions, includeNullability: false, NoLocation.Singleton, diagnostics)); + var boxedArgs = CheckConstraintsArgsBoxed.Allocate(compilation, conversions, includeNullability: false, NoLocation.Singleton, diagnostics); + type.CheckAllConstraints(boxedArgs); bool ok = !diagnostics.HasAnyErrors(); + boxedArgs.Free(); diagnostics.Free(); return ok; } @@ -518,20 +523,31 @@ public CheckConstraintsArgs(CSharpCompilation currentCompilation, ConversionsBas } } + private static readonly ObjectPool s_checkConstraintsArgsBoxedPool = new ObjectPool(static () => new CheckConstraintsArgsBoxed()); + internal sealed class CheckConstraintsArgsBoxed { - public readonly CheckConstraintsArgs Args; + public CheckConstraintsArgs Args; [MethodImpl(MethodImplOptions.NoInlining)] - public CheckConstraintsArgsBoxed(CSharpCompilation currentCompilation, ConversionsBase conversions, Location location, BindingDiagnosticBag diagnostics) + public static CheckConstraintsArgsBoxed Allocate(CSharpCompilation currentCompilation, ConversionsBase conversions, Location location, BindingDiagnosticBag diagnostics) { - Args = new CheckConstraintsArgs(currentCompilation, conversions, location, diagnostics); + var boxedArgs = s_checkConstraintsArgsBoxedPool.Allocate(); + boxedArgs.Args = new CheckConstraintsArgs(currentCompilation, conversions, location, diagnostics); + return boxedArgs; } [MethodImpl(MethodImplOptions.NoInlining)] - public CheckConstraintsArgsBoxed(CSharpCompilation currentCompilation, ConversionsBase conversions, bool includeNullability, Location location, BindingDiagnosticBag diagnostics) + public static CheckConstraintsArgsBoxed Allocate(CSharpCompilation currentCompilation, ConversionsBase conversions, bool includeNullability, Location location, BindingDiagnosticBag diagnostics) + { + var boxedArgs = s_checkConstraintsArgsBoxedPool.Allocate(); + boxedArgs.Args = new CheckConstraintsArgs(currentCompilation, conversions, includeNullability, location, diagnostics); + return boxedArgs; + } + + public void Free() { - Args = new CheckConstraintsArgs(currentCompilation, conversions, includeNullability, location, diagnostics); + s_checkConstraintsArgsBoxedPool.Free(this); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs index 6b245b6162c3b..a81dd18bc7960 100644 --- a/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/LocalSymbol.cs @@ -273,7 +273,8 @@ public bool IsForEach } /// - /// Returns the syntax node that declares the variable. + /// Returns the syntax node that declares the variable. Should always return a value if returns . May throw if it returns . /// /// /// All user-defined and long-lived synthesized variables must return a reference to a node that is @@ -284,6 +285,12 @@ public bool IsForEach /// internal abstract SyntaxNode GetDeclaratorSyntax(); + /// + /// if this has a real syntax location in source code, otherwise. + /// A common example of a local without a source location is an EE local symbol. + /// + internal abstract bool HasSourceLocation { get; } + /// /// Describes whether this represents a modifiable variable. Note that /// this refers to the variable, not the underlying value, so if this diff --git a/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs b/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs index 5dac15e864be7..1c13751b9af71 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs @@ -919,9 +919,9 @@ public override ImmutableArray AssemblyReferences } public override AssemblyReferenceBinding[] BindAssemblyReferences( - MultiDictionary assemblies, AssemblyIdentityComparer assemblyIdentityComparer) + ImmutableArray assemblies, AssemblyIdentityComparer assemblyIdentityComparer) { - return ResolveReferencedAssemblies(_referencedAssemblies, assemblies, resolveAgainstAssemblyBeingBuilt: true, assemblyIdentityComparer: assemblyIdentityComparer); + return ResolveReferencedAssemblies(_referencedAssemblies, assemblies, definitionStartIndex: 0, assemblyIdentityComparer: assemblyIdentityComparer); } public sealed override bool IsLinked diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/AttributeLocation.cs b/src/Compilers/CSharp/Portable/Symbols/Source/AttributeLocation.cs index 583e2a11971ee..bd2c122509c17 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/AttributeLocation.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/AttributeLocation.cs @@ -5,6 +5,7 @@ #nullable disable using System; +using System.Diagnostics; using System.Text; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -104,7 +105,19 @@ internal static AttributeLocation ToAttributeLocation(this SyntaxToken token) // NOTE: to match dev10, we're using the value text, rather // than the actual text. For example, "@return" is equivalent // to "return". - return ToAttributeLocation(token.ValueText); + var result = ToAttributeLocation(token.ValueText); + +#if DEBUG + var kind = SyntaxFacts.GetKeywordKind(token.ValueText); + if (kind == SyntaxKind.None) + { + kind = SyntaxFacts.GetContextualKeywordKind(token.ValueText); + } + + Debug.Assert(result == AttributeLocation.None ^ SyntaxFacts.IsAttributeTargetSpecifier(kind)); +#endif + + return result; } internal static AttributeLocation ToAttributeLocation(this Syntax.InternalSyntax.SyntaxToken token) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index e54c7008482b0..7661ae270b629 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -303,7 +303,7 @@ public override bool IsExtensionMethod public override ImmutableArray Locations => ImmutableArray.Create(Syntax.Identifier.GetLocation()); - public override Location? TryGetFirstLocation() => Syntax.Identifier.GetLocation(); + public override Location TryGetFirstLocation() => Syntax.Identifier.GetLocation(); internal override bool GenerateDebugInfo => true; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index 70a4c7370c399..efbe13b19594e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -27,7 +27,7 @@ internal static DeclarationModifiers MakeAndCheckNonTypeMemberModifiers( var readonlyToken = modifiers.FirstOrDefault(SyntaxKind.ReadOnlyKeyword); if (readonlyToken.Parent is MethodDeclarationSyntax or AccessorDeclarationSyntax or BasePropertyDeclarationSyntax or EventDeclarationSyntax) - modifierErrors |= !MessageID.IDS_FeatureReadOnlyMembers.CheckFeatureAvailability(diagnostics, readonlyToken.Parent, readonlyToken.GetLocation()); + modifierErrors |= !MessageID.IDS_FeatureReadOnlyMembers.CheckFeatureAvailability(diagnostics, readonlyToken); if ((result & DeclarationModifiers.AccessibilityMask) == 0) result |= defaultAccess; @@ -401,7 +401,7 @@ public static DeclarationModifiers ToDeclarationModifiers( if (one == DeclarationModifiers.Partial) { var messageId = isForTypeDeclaration ? MessageID.IDS_FeaturePartialTypes : MessageID.IDS_FeaturePartialMethod; - messageId.CheckFeatureAvailability(diagnostics, modifier.Parent, modifier.GetLocation()); + messageId.CheckFeatureAvailability(diagnostics, modifier); // `partial` must always be the last modifier according to the language. However, there was a bug // where we allowed `partial async` at the end of modifiers on methods. We keep this behavior for diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 4c90e7e60b40e..613f0c478e7a9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -531,7 +531,7 @@ internal static void CheckParameterModifiers( if (parsingLambdaParams) { - MessageID.IDS_FeatureLambdaParamsArray.CheckFeatureAvailability(diagnostics, parameter, modifier.GetLocation()); + MessageID.IDS_FeatureLambdaParamsArray.CheckFeatureAvailability(diagnostics, modifier); } break; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 4544f4714dc9f..e8f76dd887aea 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -367,7 +367,7 @@ private ConstantValue MakeDefaultExpression(BindingDiagnosticBag diagnostics, ou return ConstantValue.NotAvailable; } - MessageID.IDS_FeatureOptionalParameter.CheckFeatureAvailability(diagnostics, defaultSyntax, defaultSyntax.EqualsToken.GetLocation()); + MessageID.IDS_FeatureOptionalParameter.CheckFeatureAvailability(diagnostics, defaultSyntax.EqualsToken); binder = GetDefaultParameterValueBinder(defaultSyntax); Binder binderForDefault = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index ff7adaaeafb06..431d62ece58b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -25,7 +25,7 @@ protected SourceConstructorSymbolBase( Location location, CSharpSyntaxNode syntax, bool isIterator) - : base(containingType, syntax.GetReference(), ImmutableArray.Create(location), isIterator) + : base(containingType, syntax.GetReference(), location, isIterator) { Debug.Assert(syntax.Kind() is SyntaxKind.ConstructorDeclaration or SyntaxKind.RecordDeclaration or SyntaxKind.RecordStructDeclaration or SyntaxKind.ClassDeclaration or SyntaxKind.StructDeclaration); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs index 03d8434c53c3e..69ca26275e2ac 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs @@ -29,7 +29,7 @@ internal SourceCustomEventAccessorSymbol( BindingDiagnosticBag diagnostics) : base(@event, syntax.GetReference(), - ImmutableArray.Create(syntax.Keyword.GetLocation()), explicitlyImplementedEventOpt, aliasQualifierOpt, + syntax.Keyword.GetLocation(), explicitlyImplementedEventOpt, aliasQualifierOpt, isAdder: syntax.Kind() == SyntaxKind.AddAccessorDeclaration, isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), isNullableAnalysisEnabled: isNullableAnalysisEnabled) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index ef928d1c3ebd3..dc99d2054c40d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -24,13 +24,13 @@ internal abstract class SourceEventAccessorSymbol : SourceMemberMethodSymbol public SourceEventAccessorSymbol( SourceEventSymbol @event, SyntaxReference syntaxReference, - ImmutableArray locations, + Location location, EventSymbol explicitlyImplementedEventOpt, string aliasQualifierOpt, bool isAdder, bool isIterator, bool isNullableAnalysisEnabled) - : base(@event.containingType, syntaxReference, locations, isIterator) + : base(@event.containingType, syntaxReference, location, isIterator) { _event = @event; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index 9555bf9b08aeb..307ea89f5aa59 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -60,6 +60,9 @@ internal SourceEventSymbol( this.CheckAccessibility(_location, diagnostics, isExplicitInterfaceImplementation); } + public Location Location + => _location; + internal sealed override bool RequiresCompletion { get { return true; } @@ -114,6 +117,9 @@ public sealed override ImmutableArray Locations } } + public override Location TryGetFirstLocation() + => _location; + public sealed override ImmutableArray DeclaringSyntaxReferences { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs index 17ff8487a2f1c..04279e41aaf83 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs @@ -225,6 +225,9 @@ internal sealed override Location ErrorLocation public sealed override ImmutableArray DeclaringSyntaxReferences => ImmutableArray.Create(_syntaxReference); + public override bool IsDefinedInSourceTree(SyntaxTree tree, TextSpan? definedWithinSpan, CancellationToken cancellationToken = default) + => IsDefinedInSourceTree(_syntaxReference, tree, definedWithinSpan); + public sealed override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) { ref var lazyDocComment = ref expandIncludes ? ref _lazyExpandedDocComment : ref _lazyDocComment; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 746a884447a8e..9c45f1e4ec3da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -419,6 +419,8 @@ internal sealed override SyntaxNode GetDeclaratorSyntax() return _identifierToken.Parent; } + internal override bool HasSourceLocation => true; + public override ImmutableArray DeclaringSyntaxReferences { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 589b61065b511..2fea136b5a89d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -974,7 +974,7 @@ internal override LexicalSortKey GetLexicalSortKey() public sealed override ImmutableArray Locations => ImmutableArray.CastUp(declaration.NameLocations.ToImmutable()); - public override Location? TryGetFirstLocation() + public override Location TryGetFirstLocation() => declaration.Declarations[0].NameLocation; public ImmutableArray SyntaxReferences @@ -1531,7 +1531,7 @@ private Dictionary> GetEarlyAttributeDecodingMemb if (!membersAndInitializers.HaveIndexers) { - membersByName = membersAndInitializers.NonTypeMembers.ToDictionary(s => s.Name); + membersByName = membersAndInitializers.NonTypeMembers.ToDictionary(static s => s.Name); } else { @@ -1540,7 +1540,7 @@ private Dictionary> GetEarlyAttributeDecodingMemb // IndexerNameAttribute). membersByName = membersAndInitializers.NonTypeMembers. WhereAsArray(s => !s.IsIndexer() && (!s.IsAccessor() || ((MethodSymbol)s).AssociatedSymbol?.IsIndexer() != true)). - ToDictionary(s => s.Name); + ToDictionary(static s => s.Name); } AddNestedTypesToDictionary(membersByName, GetTypeMembersDictionary()); @@ -2721,7 +2721,7 @@ private Dictionary> MakeAllMembers(BindingDiagnos } else { - membersByName = membersAndInitializers.NonTypeMembers.ToDictionary(s => s.Name, StringOrdinalComparer.Instance); + membersByName = membersAndInitializers.NonTypeMembers.ToDictionary(static s => s.Name); // Merge types into the member dictionary AddNestedTypesToDictionary(membersByName, GetTypeMembersDictionary()); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 54bf08a5356cb..c053192dea854 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -196,7 +196,7 @@ internal static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingTyp foreach (var modifier in modifiers) { if (modifier.IsKind(SyntaxKind.FixedKeyword)) - MessageID.IDS_FeatureFixedBuffer.CheckFeatureAvailability(diagnostics, modifier.Parent, modifier.GetLocation()); + MessageID.IDS_FeatureFixedBuffer.CheckFeatureAvailability(diagnostics, modifier); } reportBadMemberFlagIfAny(result, DeclarationModifiers.Static, diagnostics, errorLocation); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index 5b71c9238c5c7..a6288faa15dd7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -198,7 +198,7 @@ public bool SetNullableContext(byte? value) private OverriddenOrHiddenMembersResult _lazyOverriddenOrHiddenMembers; - protected ImmutableArray locations; + protected readonly Location _location; protected string lazyDocComment; protected string lazyExpandedDocComment; @@ -218,24 +218,19 @@ internal ImmutableArray SetDiagnostics(ImmutableArray ne return _cachedDiagnostics; } - protected SourceMemberMethodSymbol(NamedTypeSymbol containingType, SyntaxReference syntaxReferenceOpt, Location location, bool isIterator) - : this(containingType, syntaxReferenceOpt, ImmutableArray.Create(location), isIterator) - { - } - protected SourceMemberMethodSymbol( NamedTypeSymbol containingType, SyntaxReference syntaxReferenceOpt, - ImmutableArray locations, + Location location, bool isIterator) : base(syntaxReferenceOpt, isIterator) { - Debug.Assert((object)containingType != null); - Debug.Assert(!locations.IsEmpty); + Debug.Assert(containingType is not null); + Debug.Assert(location is not null); Debug.Assert(containingType.DeclaringCompilation is not null); _containingType = containingType; - this.locations = locations; + _location = location; } protected void CheckEffectiveAccessibility(TypeWithAnnotations returnType, ImmutableArray parameters, BindingDiagnosticBag diagnostics) @@ -667,12 +662,10 @@ protected ExecutableCodeBinder TryGetBodyBinderFromSyntax(BinderFactory binderFa /// which might return locations of partial methods. /// public override ImmutableArray Locations - { - get - { - return this.locations; - } - } + => ImmutableArray.Create(_location); + + public override Location TryGetFirstLocation() + => _location; public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) { @@ -840,17 +833,16 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, base.AfterAddingTypeMembersChecks(conversions, diagnostics); var compilation = this.DeclaringCompilation; - var location = locations[0]; if (IsDeclaredReadOnly && !ContainingType.IsReadOnly) { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, _location, modifyCompilation: true); } if (compilation.ShouldEmitNullableAttributes(this) && ShouldEmitNullableContextValue(out _)) { - compilation.EnsureNullableContextAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableContextAttributeExists(diagnostics, _location, modifyCompilation: true); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index c861144f1cd16..01950c5021b6d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -181,7 +181,7 @@ private ImmutableArray MakeTypeParameters(BindingDiagnostic throw ExceptionUtilities.UnexpectedValue(typeDecl.Kind()); } - MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, tpl, tpl.LessThanToken.GetLocation()); + MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, tpl.LessThanToken); bool isInterfaceOrDelegate = typeKind == SyntaxKind.InterfaceDeclaration || typeKind == SyntaxKind.DelegateDeclaration; var parameterBuilder = new List(); @@ -197,7 +197,7 @@ private ImmutableArray MakeTypeParameters(BindingDiagnostic } else { - MessageID.IDS_FeatureTypeVariance.CheckFeatureAvailability(diagnostics, tp, tp.VarianceKeyword.GetLocation()); + MessageID.IDS_FeatureTypeVariance.CheckFeatureAvailability(diagnostics, tp.VarianceKeyword); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs index 483eefcfd7fa2..bdc398d4ddf93 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs @@ -86,10 +86,10 @@ private SingleNamespaceDeclaration GetMatchingNamespaceDeclaration(CSharpSyntaxN } private static AliasesAndUsings GetOrCreateAliasAndUsings( - ref ImmutableSegmentedDictionary dictionary, + ref ImmutableDictionary dictionary, SingleNamespaceDeclaration declaration) { - return RoslynImmutableInterlocked.GetOrAdd( + return ImmutableInterlocked.GetOrAdd( ref dictionary, declaration, static _ => new AliasesAndUsings()); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs index 260750c16f46c..a8072e38d45d4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs @@ -19,6 +19,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed partial class SourceNamespaceSymbol : NamespaceSymbol { + private static readonly ImmutableDictionary s_emptyMap = + ImmutableDictionary.Empty.WithComparers(ReferenceEqualityComparer.Instance); + private readonly SourceModuleSymbol _module; private readonly Symbol _container; private readonly MergedNamespaceDeclaration _mergedDeclaration; @@ -33,14 +36,12 @@ internal sealed partial class SourceNamespaceSymbol : NamespaceSymbol /// /// Should only be read using . /// - private ImmutableSegmentedDictionary _aliasesAndUsings_doNotAccessDirectly = - ImmutableSegmentedDictionary.Empty.WithComparer(ReferenceEqualityComparer.Instance); + private ImmutableDictionary _aliasesAndUsings_doNotAccessDirectly = s_emptyMap; #if DEBUG /// /// Should only be read using . /// - private ImmutableSegmentedDictionary _aliasesAndUsingsForAsserts_doNotAccessDirectly = - ImmutableSegmentedDictionary.Empty.WithComparer(ReferenceEqualityComparer.Instance); + private ImmutableDictionary _aliasesAndUsingsForAsserts_doNotAccessDirectly = s_emptyMap; #endif private MergedGlobalAliasesAndUsings _lazyMergedGlobalAliasesAndUsings; @@ -102,6 +103,19 @@ public override ImmutableArray Locations public override Location TryGetFirstLocation() => _mergedDeclaration.Declarations[0].NameLocation; + public override bool HasLocationContainedWithin(SyntaxTree tree, TextSpan declarationSpan, out bool wasZeroWidthMatch) + { + // Avoid the allocation of .Locations in the base method. + foreach (var decl in _mergedDeclaration.Declarations) + { + if (IsLocationContainedWithin(decl.NameLocation, tree, declarationSpan, out wasZeroWidthMatch)) + return true; + } + + wasZeroWidthMatch = false; + return false; + } + private static readonly Func s_declaringSyntaxReferencesSelector = d => new NamespaceDeclarationSyntaxReference(d.SyntaxReference); @@ -300,13 +314,14 @@ private Dictionary> MakeNameToMemb // NOTE: a name maps into values collection containing types only instead of allocating another // NOTE: array of NamedTypeSymbol[] we downcast the array to ImmutableArray - var builder = new NameToSymbolMapBuilder(_mergedDeclaration.Children.Length); + var builder = new NameToSymbolMapBuilder(); foreach (var declaration in _mergedDeclaration.Children) { builder.Add(BuildSymbol(declaration, diagnostics)); } var result = builder.CreateMap(); + builder.Free(); CheckMembers(this, result, diagnostics); @@ -502,11 +517,15 @@ private void RegisterDeclaredCorTypes() private readonly struct NameToSymbolMapBuilder { - private readonly Dictionary _dictionary; + private readonly PooledDictionary _dictionary = PooledDictionary.GetInstance(); + + public NameToSymbolMapBuilder() + { + } - public NameToSymbolMapBuilder(int capacity) + public void Free() { - _dictionary = new Dictionary(capacity, StringOrdinalComparer.Instance); + _dictionary.Free(); } public void Add(NamespaceOrTypeSymbol symbol) @@ -532,7 +551,7 @@ public void Add(NamespaceOrTypeSymbol symbol) public Dictionary> CreateMap() { - var result = new Dictionary>(_dictionary.Count, StringOrdinalComparer.Instance); + var result = new Dictionary>(_dictionary.Count); foreach (var kvp in _dictionary) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs index 9236831630301..d52af621917fc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs @@ -48,11 +48,10 @@ public sealed override bool ReturnsVoid this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); this.CheckFileTypeUsage(_lazyReturnType, _lazyParameters, diagnostics); - var location = locations[0]; // Checks taken from MemberDefiner::defineMethod if (this.Name == WellKnownMemberNames.DestructorName && this.ParameterCount == 0 && this.Arity == 0 && this.ReturnsVoid) { - diagnostics.Add(ErrorCode.WRN_FinalizeMethod, location); + diagnostics.Add(ErrorCode.WRN_FinalizeMethod, _location); } ExtensionMethodChecks(diagnostics); @@ -61,12 +60,12 @@ public sealed override bool ReturnsVoid { if (MethodKind == MethodKind.ExplicitInterfaceImplementation) { - diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, location); + diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, _location); } if (!ContainingType.IsPartial()) { - diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyInPartialClass, location); + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyInPartialClass, _location); } } @@ -228,11 +227,10 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { base.AfterAddingTypeMembersChecks(conversions, diagnostics); - var location = ReturnTypeLocation; + // Defer computing location to avoid unnecessary allocations in most cases. + Location? returnTypeLocation = null; var compilation = DeclaringCompilation; - Debug.Assert(location != null); - // Check constraints on return type and parameters. Note: Dev10 uses the // method name location for any such errors. We'll do the same for return // type errors but for parameter errors, we'll use the parameter location. @@ -249,14 +247,14 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (RefKind == RefKind.RefReadOnly) { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(ReturnType)) { - compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNativeIntegerAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); @@ -265,10 +263,16 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) { - compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + + Location getReturnTypeLocation() + { + returnTypeLocation ??= this.ReturnTypeLocation; + return returnTypeLocation; + } } protected abstract void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index aadde6cc87875..55127be6d829e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -217,7 +217,6 @@ protected override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) if (IsExtensionMethod) { var syntax = GetSyntax(); - var location = locations[0]; var parameter0Type = this.Parameters[0].TypeWithAnnotations; var parameter0RefKind = this.Parameters[0].RefKind; if (!parameter0Type.Type.IsValidExtensionParameterType()) @@ -230,15 +229,15 @@ protected override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) } else if (parameter0RefKind == RefKind.Ref && !parameter0Type.Type.IsValueType) { - diagnostics.Add(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, location, Name); + diagnostics.Add(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, _location, Name); } else if (parameter0RefKind == RefKind.In && parameter0Type.TypeKind != TypeKind.Struct) { - diagnostics.Add(ErrorCode.ERR_InExtensionMustBeValueType, location, Name); + diagnostics.Add(ErrorCode.ERR_InExtensionMustBeValueType, _location, Name); } else if ((object)ContainingType.ContainingType != null) { - diagnostics.Add(ErrorCode.ERR_ExtensionMethodsDecl, location, ContainingType.Name); + diagnostics.Add(ErrorCode.ERR_ExtensionMethodsDecl, _location, ContainingType.Name); } else if (!ContainingType.IsScriptClass && !(ContainingType.IsStatic && ContainingType.Arity == 0)) { @@ -251,7 +250,7 @@ protected override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) } else if (!IsStatic) { - diagnostics.Add(ErrorCode.ERR_BadExtensionMeth, location); + diagnostics.Add(ErrorCode.ERR_BadExtensionMeth, _location); } else { @@ -533,7 +532,7 @@ private ImmutableArray MakeTypeParameters(MethodDeclaration { Debug.Assert(syntax.TypeParameterList != null); - MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, syntax.TypeParameterList, syntax.TypeParameterList.LessThanToken.GetLocation()); + MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, syntax.TypeParameterList.LessThanToken); OverriddenMethodTypeParameterMapBase typeMap = null; if (this.IsOverride) @@ -620,8 +619,9 @@ public override bool IsDefinedInSourceTree( { // Since only the declaring (and not the implementing) part of a partial method appears in the member // list, we need to ensure we complete the implementation part when needed. + Debug.Assert(this.DeclaringSyntaxReferences.Length == 1); return - base.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken) || + IsDefinedInSourceTree(this.SyntaxRef, tree, definedWithinSpan) || this.SourcePartialImplementation?.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken) == true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index b1fabb6d3048b..9769624caabcf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -124,7 +124,7 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) } } - CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, isVararg, HasAnyBody, locations[0], diagnostics); + CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, isVararg, HasAnyBody, _location, diagnostics); } #nullable disable @@ -176,14 +176,6 @@ public override ImmutableArray TypeParameters get { return _typeParameters; } } - public override ImmutableArray Locations - { - get - { - return this.locations; - } - } - public abstract override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)); public override string Name diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 9bf97d566b7ad..c1c7d572ce5d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -235,7 +235,7 @@ protected SourcePropertyAccessorSymbol( } if (modifiers.Count > 0) - MessageID.IDS_FeaturePropertyAccessorMods.CheckFeatureAvailability(diagnostics, syntax, modifiers[0].GetLocation()); + MessageID.IDS_FeaturePropertyAccessorMods.CheckFeatureAvailability(diagnostics, modifiers[0]); } #nullable disable @@ -390,7 +390,7 @@ private TypeWithAnnotations ComputeReturnType(BindingDiagnosticBag diagnostics) if (IsInitOnly) { var isInitOnlyType = Binder.GetWellKnownType(this.DeclaringCompilation, - WellKnownType.System_Runtime_CompilerServices_IsExternalInit, diagnostics, this.locations[0]); + WellKnownType.System_Runtime_CompilerServices_IsExternalInit, diagnostics, _location); var modifiers = ImmutableArray.Create( CSharpCustomModifier.CreateRequired(isInitOnlyType)); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index a2247924b101e..af93d12c934ff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -133,7 +133,7 @@ private SourcePropertySymbol( diagnostics); if (syntax is PropertyDeclarationSyntax { Initializer: { } initializer }) - MessageID.IDS_FeatureAutoPropertyInitializer.CheckFeatureAvailability(diagnostics, syntax, initializer.EqualsToken.GetLocation()); + MessageID.IDS_FeatureAutoPropertyInitializer.CheckFeatureAvailability(diagnostics, initializer.EqualsToken); } private TypeSyntax GetTypeSyntax(SyntaxNode syntax) => ((BasePropertyDeclarationSyntax)syntax).Type; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs index 120d9317d75bc..9e1b9731d1468 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceTypeParameterSymbol.cs @@ -258,7 +258,8 @@ private void CheckConstraintTypeConstraints(BindingDiagnosticBag diagnostics) return; } - var args = new ConstraintsHelper.CheckConstraintsArgsBoxed(DeclaringCompilation, new TypeConversions(ContainingAssembly.CorLibrary), _locations[0], diagnostics); + var args = ConstraintsHelper.CheckConstraintsArgsBoxed.Allocate( + DeclaringCompilation, new TypeConversions(ContainingAssembly.CorLibrary), _locations[0], diagnostics); foreach (var constraintType in constraintTypes) { if (!diagnostics.ReportUseSite(constraintType.Type, args.Args.Location)) @@ -266,6 +267,8 @@ private void CheckConstraintTypeConstraints(BindingDiagnosticBag diagnostics) constraintType.Type.CheckAllConstraints(args); } } + + args.Free(); } private void CheckUnmanagedConstraint(BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 136872cc5bb72..0e9bac249c720 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -27,7 +27,7 @@ public static SourceUserDefinedConversionSymbol CreateUserDefinedConversionSymbo if (name == WellKnownMemberNames.CheckedExplicitConversionName) { - MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax, syntax.CheckedKeyword.GetLocation()); + MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax.CheckedKeyword); } else if (syntax.CheckedKeyword.IsKind(SyntaxKind.CheckedKeyword)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index b150708e7de10..4a9c89330c33f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -27,7 +27,7 @@ public static SourceUserDefinedOperatorSymbol CreateUserDefinedOperatorSymbol( if (SyntaxFacts.IsCheckedOperator(name)) { - MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax, syntax.CheckedKeyword.GetLocation()); + MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax.CheckedKeyword); } else if (!syntax.OperatorToken.IsMissing && syntax.CheckedKeyword.IsKind(SyntaxKind.CheckedKeyword)) { @@ -36,7 +36,7 @@ public static SourceUserDefinedOperatorSymbol CreateUserDefinedOperatorSymbol( if (name == WellKnownMemberNames.UnsignedRightShiftOperatorName) { - MessageID.IDS_FeatureUnsignedRightShift.CheckFeatureAvailability(diagnostics, syntax, syntax.OperatorToken.GetLocation()); + MessageID.IDS_FeatureUnsignedRightShift.CheckFeatureAvailability(diagnostics, syntax.OperatorToken); } var interfaceSpecifier = syntax.ExplicitInterfaceSpecifier; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index 6da0044b48859..973254524cca1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -779,14 +779,6 @@ public sealed override bool IsExtensionMethod } } - public sealed override ImmutableArray Locations - { - get - { - return this.locations; - } - } - public sealed override ImmutableArray TypeParameters { get { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 734dba9e066e3..17a55ad69b6d8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -349,6 +349,36 @@ public Location GetFirstLocation() public Location GetFirstLocationOrNone() => TryGetFirstLocation() ?? Location.None; + /// + /// Determines if there is a location (see ) for this symbol whose span is in and is contained within . Subclasses can override this to + /// be more efficient if desired (especially if avoiding allocations of the array is + /// desired). + /// + public virtual bool HasLocationContainedWithin(SyntaxTree tree, TextSpan declarationSpan, out bool wasZeroWidthMatch) + { + foreach (var loc in this.Locations) + { + if (IsLocationContainedWithin(loc, tree, declarationSpan, out wasZeroWidthMatch)) + return true; + } + + wasZeroWidthMatch = false; + return false; + } + + protected static bool IsLocationContainedWithin(Location loc, SyntaxTree tree, TextSpan declarationSpan, out bool wasZeroWidthMatch) + { + if (loc.IsInSource && loc.SourceTree == tree && declarationSpan.Contains(loc.SourceSpan)) + { + wasZeroWidthMatch = loc.SourceSpan.IsEmpty && loc.SourceSpan.End == declarationSpan.Start; + return true; + } + + wasZeroWidthMatch = false; + return false; + } + #nullable disable /// @@ -829,8 +859,7 @@ internal bool Dangerous_IsFromSomeCompilation { cancellationToken.ThrowIfCancellationRequested(); - if (syntaxRef.SyntaxTree == tree && - (!definedWithinSpan.HasValue || syntaxRef.Span.IntersectsWith(definedWithinSpan.Value))) + if (IsDefinedInSourceTree(syntaxRef, tree, definedWithinSpan)) { return true; } @@ -839,6 +868,10 @@ internal bool Dangerous_IsFromSomeCompilation return false; } + protected static bool IsDefinedInSourceTree(SyntaxReference syntaxRef, SyntaxTree tree, TextSpan? definedWithinSpan) + => syntaxRef.SyntaxTree == tree && + (!definedWithinSpan.HasValue || syntaxRef.Span.IntersectsWith(definedWithinSpan.Value)); + internal static void ForceCompleteMemberByLocation(SourceLocation locationOpt, Symbol member, CancellationToken cancellationToken) { if (locationOpt == null || member.IsDefinedInSourceTree(locationOpt.SourceTree, locationOpt.SourceSpan, cancellationToken)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs index df757b05d83d7..e165c6b2f3da6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs @@ -25,7 +25,7 @@ internal sealed class SynthesizedEventAccessorSymbol : SourceEventAccessorSymbol private readonly object _methodChecksLockObject = new object(); internal SynthesizedEventAccessorSymbol(SourceEventSymbol @event, bool isAdder, EventSymbol explicitlyImplementedEventOpt = null, string aliasQualifierOpt = null) - : base(@event, null, @event.Locations, explicitlyImplementedEventOpt, aliasQualifierOpt, isAdder, isIterator: false, isNullableAnalysisEnabled: false) + : base(@event, null, @event.Location, explicitlyImplementedEventOpt, aliasQualifierOpt, isAdder, isIterator: false, isNullableAnalysisEnabled: false) { } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs index 604f3b675b906..6499b709e10c1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs @@ -156,6 +156,9 @@ internal sealed override SyntaxNode GetDeclaratorSyntax() return _syntaxOpt; } + internal override bool HasSourceLocation + => _syntaxOpt != null; + public sealed override bool IsImplicitlyDeclared { get { return true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs index 58ad02dc07584..232f54a690883 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSimpleProgramEntryPointSymbol.cs @@ -26,7 +26,7 @@ internal sealed class SynthesizedSimpleProgramEntryPointSymbol : SourceMemberMet private WeakReference? _weakIgnoreAccessibilityBodyBinder; internal SynthesizedSimpleProgramEntryPointSymbol(SourceMemberContainerTypeSymbol containingType, SingleTypeDeclaration declaration, BindingDiagnosticBag diagnostics) - : base(containingType, syntaxReferenceOpt: declaration.SyntaxReference, ImmutableArray.Create(declaration.SyntaxReference.GetLocation()), isIterator: declaration.IsIterator) + : base(containingType, syntaxReferenceOpt: declaration.SyntaxReference, declaration.SyntaxReference.GetLocation(), isIterator: declaration.IsIterator) { Debug.Assert(declaration.SyntaxReference.GetSyntax() is CompilationUnitSyntax); _declaration = declaration; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs index a0cd6788629cd..22131b229323a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/TypeSubstitutedLocalSymbol.cs @@ -69,6 +69,9 @@ internal override SyntaxNode GetDeclaratorSyntax() return _originalVariable.GetDeclaratorSyntax(); } + internal override bool HasSourceLocation + => _originalVariable.HasSourceLocation; + public override ImmutableArray Locations { get { return _originalVariable.Locations; } diff --git a/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs b/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs index dc1292c702d08..ed02a75793f5f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs +++ b/src/Compilers/CSharp/Portable/Symbols/UpdatedContainingSymbolLocal.cs @@ -96,6 +96,8 @@ internal override ImmutableBindingDiagnostic GetConstantValueDia _underlyingLocal.GetConstantValueDiagnostics(boundInitValue); internal override SyntaxNode GetDeclaratorSyntax() => _underlyingLocal.GetDeclaratorSyntax(); + internal override bool HasSourceLocation + => _underlyingLocal.HasSourceLocation; internal override LocalSymbol WithSynthesizedLocalKindAndSyntax( SynthesizedLocalKind kind, SyntaxNode syntax #if DEBUG diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs index 2bd370437e2c1..aa2d4ea5f0831 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs @@ -154,7 +154,7 @@ public override string GetDocumentationCommentXml(CultureInfo? preferredCulture internal sealed override ScopedKind EffectiveScope => _underlyingParameter.EffectiveScope; - internal override bool HasUnscopedRefAttribute => _underlyingParameter.HasUnscopedRefAttribute; + internal sealed override bool HasUnscopedRefAttribute => _underlyingParameter.HasUnscopedRefAttribute; internal sealed override bool UseUpdatedEscapeRules => _underlyingParameter.UseUpdatedEscapeRules; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index d6624d597b04b..652092a89b94f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -45,6 +45,14 @@ public static bool IsAttributeTargetSpecifier(SyntaxKind kind) { case SyntaxKind.AssemblyKeyword: case SyntaxKind.ModuleKeyword: + case SyntaxKind.EventKeyword: + case SyntaxKind.FieldKeyword: + case SyntaxKind.MethodKeyword: + case SyntaxKind.ParamKeyword: + case SyntaxKind.PropertyKeyword: + case SyntaxKind.ReturnKeyword: + case SyntaxKind.TypeKeyword: + case SyntaxKind.TypeVarKeyword: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index c181404184893..f315f0a24792a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -271,10 +271,10 @@ private static TypeSyntax SkipRefWorker(TypeSyntax syntax, BindingDiagnosticBag? (current.Parent is VariableDeclarationSyntax { Parent: LocalDeclarationStatementSyntax } variableDeclaration && variableDeclaration.Type == current)); #endif - MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability(diagnostics, refType, refType.RefKeyword.GetLocation()); + MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability(diagnostics, refType.RefKeyword); if (refType.ReadOnlyKeyword != default) - MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, refType, refType.ReadOnlyKeyword.GetLocation()); + MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, refType.ReadOnlyKeyword); } return refType.Type; @@ -325,8 +325,7 @@ internal static SyntaxNode ModifyingScopedOrRefTypeOrSelf(this SyntaxNode syntax return syntax; } - MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability( - diagnostics, refExpression, refExpression.RefKeyword.GetLocation()); + MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability(diagnostics, refExpression.RefKeyword); refKind = RefKind.Ref; expression.CheckDeconstructionCompatibleArgument(diagnostics); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 9eec4895c74af..cf2d5091ce7e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1907,6 +1907,11 @@ odvozený typ delegáta + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes atributy lambda diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index f419ece90e2c2..d1aa50feea941 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1907,6 +1907,11 @@ abgeleiteter Delegattyp + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes Lambdaattribute diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 0434ca386a6b7..1d7eae3b86136 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1907,6 +1907,11 @@ tipo de delegado inferido + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes atributos de lambda diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 11a4c75e46504..d9515827f9d61 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1907,6 +1907,11 @@ type délégué déduit + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes attributs lambda diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ca1540f042777..b9599ffc38273 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1907,6 +1907,11 @@ tipo di delegato dedotto + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes attributi lambda diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 561713d66a430..edc79dc35b41a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1907,6 +1907,11 @@ 推論されたデリゲート型 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes ラムダ属性 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 128a8a2b7a1eb..8ba51bf19c702 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1907,6 +1907,11 @@ 유추된 대리자 형식 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes 람다 특성 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index c228fd971f016..d50d0d8303084 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1907,6 +1907,11 @@ Typ delegowania wywnioskowanego + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes atrybuty lambda diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 3c63e31f63721..6b2d4acb4bb83 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1907,6 +1907,11 @@ tipo representante inferido + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes lambda attributes diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index a82d18a9c3940..d1362cd391fc4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1907,6 +1907,11 @@ выводимый тип делегата + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes лямбда-атрибуты diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 6ce875379dc5b..63258ad292658 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1907,6 +1907,11 @@ çıkarsanan temsilci türü + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes lambda öznitelikleri diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index cbfe6c7f76eb0..80a8f77bb0f6b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1907,6 +1907,11 @@ 推断的委托类型 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes Lambda 属性 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 15c8a3eb6db39..04a3ec570e8e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1907,6 +1907,11 @@ 推斷委派類型 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes Lambda 屬性 diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 8731f0293ac95..67a700515e295 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -3516,6 +3516,81 @@ public static void Main() itemSeparator: "\r\n"); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47310")] + public void DiagnosticFormatting_UrlFormat_ObsoleteAttribute() + { + var dir = Temp.CreateDirectory(); + var file = dir.CreateFile("a.cs"); + file.WriteAllText(""" + #pragma warning disable CS0436 // System.Obsolete conflict + #nullable enable + using System; + + var c1 = new C1(); + var c2 = new C2(); + var c3 = new C3(); + var c4 = new C4(); + + [Obsolete("Do not use C1", UrlFormat = "https://example.org/{0}")] + public class C1 { } + [Obsolete("Do not use C2", error: true, UrlFormat = "https://example.org/2/{0}")] + public class C2 { } + [Obsolete("Do not use C3", error: true, DiagnosticId = "OBSOLETEC3", UrlFormat = "https://example.org/3/{0}")] + public class C3 { } + [Obsolete("Do not use C4", DiagnosticId = "OBSOLETEC4", UrlFormat = "https://example.org/4")] + public class C4 { } + + namespace System + { + public class ObsoleteAttribute : Attribute + { + public ObsoleteAttribute() { } + public ObsoleteAttribute(string? message) { } + public ObsoleteAttribute(string? message, bool error) { } + + public string? DiagnosticId { get; set; } + public string? UrlFormat { get; set; } + } + } + """); + + var output = VerifyOutput(dir, file, + includeCurrentAssemblyAsAnalyzerReference: false, + expectedWarningCount: 2, + expectedErrorCount: 2, + additionalFlags: new[] { "/t:exe" }); + + AssertEx.Equal(""" + a.cs(5,14): warning CS0618: 'C1' is obsolete: 'Do not use C1' (https://example.org/CS0618) + a.cs(6,14): error CS0619: 'C2' is obsolete: 'Do not use C2' (https://example.org/2/CS0619) + a.cs(7,14): error OBSOLETEC3: 'C3' is obsolete: 'Do not use C3' (https://example.org/3/OBSOLETEC3) + a.cs(8,14): warning OBSOLETEC4: 'C4' is obsolete: 'Do not use C4' (https://example.org/4) + """, + output.Trim()); + + CleanupAllGeneratedFiles(file.Path); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47310")] + public void DiagnosticFormatting_DiagnosticAnalyzer() + { + var dir = Temp.CreateDirectory(); + var file = dir.CreateFile("a.cs"); + file.WriteAllText("class C { }"); + + var output = VerifyOutput(dir, file, + includeCurrentAssemblyAsAnalyzerReference: false, + expectedWarningCount: 1, + analyzers: new[] { new WarningWithUrlDiagnosticAnalyzer() }); + + AssertEx.Equal(""" + a.cs(1,7): warning Warning02: Throwing a diagnostic for types declared (https://example.org/analyzer) + """, + output.Trim()); + + CleanupAllGeneratedFiles(file.Path); + } + [WorkItem(540891, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540891")] [ConditionalFact(typeof(WindowsDesktopOnly), Reason = "https://github.com/dotnet/roslyn/issues/30289")] public void ParseOut() @@ -8635,7 +8710,7 @@ public void FileShareDeleteCompatibility_Windows() fsDll.Dispose(); fsPdb.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); } /// @@ -8692,7 +8767,7 @@ public void FileShareDeleteCompatibility_Xplat() peDll.Dispose(); pePdb.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); // files can be deleted now: File.Delete(libSrc.Path); @@ -8733,7 +8808,7 @@ public void FileShareDeleteCompatibility_ReadOnlyFiles() fsDll.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); } [Fact] @@ -14712,6 +14787,23 @@ public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisCon } } + internal class WarningWithUrlDiagnosticAnalyzer : CompilationStartedAnalyzer + { + internal static readonly DiagnosticDescriptor Warning02 = new DiagnosticDescriptor("Warning02", "", "Throwing a diagnostic for types declared", "", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://example.org/analyzer"); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Warning02); + + public override void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext context) + { + context.RegisterSymbolAction( + static (symbolContext) => + { + symbolContext.ReportDiagnostic(Diagnostic.Create(Warning02, symbolContext.Symbol.Locations.First())); + }, + SymbolKind.NamedType); + } + } + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] internal class ErrorDiagnosticAnalyzer : CompilationStartedAnalyzer { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs index 074ccda8e2bd0..d3fcb31920f89 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs @@ -13,6 +13,588 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen { public class CodeGenConditionalOperatorTests : CSharpTestBase { + [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] + public void Branchless_Compare() + { + var source = """ + using static System.Console; + WriteLine(C.Compare(1, 2)); + WriteLine(C.Compare(3, 3)); + WriteLine(C.Compare(5, 4)); + + class C + { + public static int Compare(int x, int y) + { + int tmp1 = (x > y) ? 1 : 0; + int tmp2 = (x < y) ? 1 : 0; + return tmp1 - tmp2; + } + } + """; + var expectedOutput = """ + -1 + 0 + 1 + """; + + var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.Compare", """ + { + // Code size 12 (0xc) + .maxstack 3 + .locals init (int V_0) //tmp2 + // sequence point: int tmp1 = (x > y) ? 1 : 0; + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: cgt + // sequence point: int tmp2 = (x < y) ? 1 : 0; + IL_0004: ldarg.0 + IL_0005: ldarg.1 + IL_0006: clt + IL_0008: stloc.0 + // sequence point: return tmp1 - tmp2; + IL_0009: ldloc.0 + IL_000a: sub + IL_000b: ret + } + """); + + verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe.WithDebugPlusMode(true), expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.Compare", """ + { + // Code size 14 (0xe) + .maxstack 2 + .locals init (int V_0, //tmp1 + int V_1) //tmp2 + // sequence point: int tmp1 = (x > y) ? 1 : 0; + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: cgt + IL_0004: stloc.0 + // sequence point: int tmp2 = (x < y) ? 1 : 0; + IL_0005: ldarg.0 + IL_0006: ldarg.1 + IL_0007: clt + IL_0009: stloc.1 + // sequence point: return tmp1 - tmp2; + IL_000a: ldloc.0 + IL_000b: ldloc.1 + IL_000c: sub + IL_000d: ret + } + """); + + verifier = CompileAndVerify(source, options: TestOptions.DebugExe, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.Compare", """ + { + // Code size 27 (0x1b) + .maxstack 2 + .locals init (int V_0, //tmp1 + int V_1, //tmp2 + int V_2) + // sequence point: { + IL_0000: nop + // sequence point: int tmp1 = (x > y) ? 1 : 0; + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: bgt.s IL_0008 + IL_0005: ldc.i4.0 + IL_0006: br.s IL_0009 + IL_0008: ldc.i4.1 + IL_0009: stloc.0 + // sequence point: int tmp2 = (x < y) ? 1 : 0; + IL_000a: ldarg.0 + IL_000b: ldarg.1 + IL_000c: blt.s IL_0011 + IL_000e: ldc.i4.0 + IL_000f: br.s IL_0012 + IL_0011: ldc.i4.1 + IL_0012: stloc.1 + // sequence point: return tmp1 - tmp2; + IL_0013: ldloc.0 + IL_0014: ldloc.1 + IL_0015: sub + IL_0016: stloc.2 + IL_0017: br.s IL_0019 + // sequence point: } + IL_0019: ldloc.2 + IL_001a: ret + } + """); + } + + [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] + public void Branchless_Operations() + { + var source = """ + using static System.Console; + + class C + { + static void Main() => M(1, 0, null!, true); + static void M(int x, int y, object a, bool b) + { + Write(x == y ? 1 : 0); + Write(x == y ? 0 : 1); + Write(x < y ? 1 : 0); + Write(x < y ? 0 : 1); + Write(x > y ? 1 : 0); + Write(x > y ? 0 : 1); + Write(a is string ? 0 : 1); + Write(a is not string ? 0 : 1); + Write(b ? 0 : 1); + Write(!b ? 0 : 1); + Write(x <= y ? true : false); + Write(x <= y ? false : true); + Write(x != y ? (byte)1 : (byte)0); + Write(x != y ? 1 : (sbyte)0); + Write(x != y ? (short)1 : (short)0); + Write(x != y ? (ushort)1 : 0); + Write(x != y ? 1U : 0); + Write(x != y ? 1L : 0); + Write(x != y ? 1UL : 0); + Write(x != y ? (nint)1 : 0); + Write(x != y ? 1 : (nuint)0); + Write(x < y ? (char)0 : (char)1); + Write(x < y ? '\x1' : '\x0'); + Write(true ? 1 : 0); + Write(false ? 0 : 1); + const bool B = true; + Write(B ? 1 : 0); + } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.ReleaseExe, + expectedOutput: "0101101001FalseTrue111111111" + (char)1 + (char)0 + "111"); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.M", """ + { + // Code size 288 (0x120) + .maxstack 2 + // sequence point: Write(x == y ? 1 : 0); + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: ceq + IL_0004: call "void System.Console.Write(int)" + // sequence point: Write(x == y ? 0 : 1); + IL_0009: ldarg.0 + IL_000a: ldarg.1 + IL_000b: ceq + IL_000d: ldc.i4.0 + IL_000e: ceq + IL_0010: call "void System.Console.Write(int)" + // sequence point: Write(x < y ? 1 : 0); + IL_0015: ldarg.0 + IL_0016: ldarg.1 + IL_0017: clt + IL_0019: call "void System.Console.Write(int)" + // sequence point: Write(x < y ? 0 : 1); + IL_001e: ldarg.0 + IL_001f: ldarg.1 + IL_0020: clt + IL_0022: ldc.i4.0 + IL_0023: ceq + IL_0025: call "void System.Console.Write(int)" + // sequence point: Write(x > y ? 1 : 0); + IL_002a: ldarg.0 + IL_002b: ldarg.1 + IL_002c: cgt + IL_002e: call "void System.Console.Write(int)" + // sequence point: Write(x > y ? 0 : 1); + IL_0033: ldarg.0 + IL_0034: ldarg.1 + IL_0035: cgt + IL_0037: ldc.i4.0 + IL_0038: ceq + IL_003a: call "void System.Console.Write(int)" + // sequence point: Write(a is string ? 0 : 1); + IL_003f: ldarg.2 + IL_0040: isinst "string" + IL_0045: ldnull + IL_0046: ceq + IL_0048: call "void System.Console.Write(int)" + // sequence point: Write(a is not string ? 0 : 1); + IL_004d: ldarg.2 + IL_004e: isinst "string" + IL_0053: ldnull + IL_0054: cgt.un + IL_0056: call "void System.Console.Write(int)" + // sequence point: Write(b ? 0 : 1); + IL_005b: ldarg.3 + IL_005c: ldc.i4.0 + IL_005d: ceq + IL_005f: call "void System.Console.Write(int)" + // sequence point: Write(!b ? 0 : 1); + IL_0064: ldarg.3 + IL_0065: ldc.i4.0 + IL_0066: cgt.un + IL_0068: call "void System.Console.Write(int)" + // sequence point: Write(x <= y ? true : false); + IL_006d: ldarg.0 + IL_006e: ldarg.1 + IL_006f: cgt + IL_0071: ldc.i4.0 + IL_0072: ceq + IL_0074: call "void System.Console.Write(bool)" + // sequence point: Write(x <= y ? false : true); + IL_0079: ldarg.0 + IL_007a: ldarg.1 + IL_007b: cgt + IL_007d: call "void System.Console.Write(bool)" + // sequence point: Write(x != y ? (byte)1 : (byte)0); + IL_0082: ldarg.0 + IL_0083: ldarg.1 + IL_0084: ceq + IL_0086: ldc.i4.0 + IL_0087: ceq + IL_0089: conv.u1 + IL_008a: call "void System.Console.Write(int)" + // sequence point: Write(x != y ? 1 : (sbyte)0); + IL_008f: ldarg.0 + IL_0090: ldarg.1 + IL_0091: ceq + IL_0093: ldc.i4.0 + IL_0094: ceq + IL_0096: call "void System.Console.Write(int)" + // sequence point: Write(x != y ? (short)1 : (short)0); + IL_009b: ldarg.0 + IL_009c: ldarg.1 + IL_009d: ceq + IL_009f: ldc.i4.0 + IL_00a0: ceq + IL_00a2: conv.i2 + IL_00a3: call "void System.Console.Write(int)" + // sequence point: Write(x != y ? (ushort)1 : 0); + IL_00a8: ldarg.0 + IL_00a9: ldarg.1 + IL_00aa: ceq + IL_00ac: ldc.i4.0 + IL_00ad: ceq + IL_00af: call "void System.Console.Write(int)" + // sequence point: Write(x != y ? 1U : 0); + IL_00b4: ldarg.0 + IL_00b5: ldarg.1 + IL_00b6: ceq + IL_00b8: ldc.i4.0 + IL_00b9: ceq + IL_00bb: call "void System.Console.Write(uint)" + // sequence point: Write(x != y ? 1L : 0); + IL_00c0: ldarg.0 + IL_00c1: ldarg.1 + IL_00c2: ceq + IL_00c4: ldc.i4.0 + IL_00c5: ceq + IL_00c7: conv.i8 + IL_00c8: call "void System.Console.Write(long)" + // sequence point: Write(x != y ? 1UL : 0); + IL_00cd: ldarg.0 + IL_00ce: ldarg.1 + IL_00cf: ceq + IL_00d1: ldc.i4.0 + IL_00d2: ceq + IL_00d4: conv.i8 + IL_00d5: call "void System.Console.Write(ulong)" + // sequence point: Write(x != y ? (nint)1 : 0); + IL_00da: ldarg.0 + IL_00db: ldarg.1 + IL_00dc: ceq + IL_00de: ldc.i4.0 + IL_00df: ceq + IL_00e1: conv.i + IL_00e2: conv.i8 + IL_00e3: call "void System.Console.Write(long)" + // sequence point: Write(x != y ? 1 : (nuint)0); + IL_00e8: ldarg.0 + IL_00e9: ldarg.1 + IL_00ea: ceq + IL_00ec: ldc.i4.0 + IL_00ed: ceq + IL_00ef: conv.i + IL_00f0: conv.u8 + IL_00f1: call "void System.Console.Write(ulong)" + // sequence point: Write(x < y ? (char)0 : (char)1); + IL_00f6: ldarg.0 + IL_00f7: ldarg.1 + IL_00f8: clt + IL_00fa: ldc.i4.0 + IL_00fb: ceq + IL_00fd: conv.u2 + IL_00fe: call "void System.Console.Write(char)" + // sequence point: Write(x < y ? '\x1' : '\x0'); + IL_0103: ldarg.0 + IL_0104: ldarg.1 + IL_0105: clt + IL_0107: conv.u2 + IL_0108: call "void System.Console.Write(char)" + // sequence point: Write(true ? 1 : 0); + IL_010d: ldc.i4.1 + IL_010e: call "void System.Console.Write(int)" + // sequence point: Write(false ? 0 : 1); + IL_0113: ldc.i4.1 + IL_0114: call "void System.Console.Write(int)" + // sequence point: Write(B ? 1 : 0); + IL_0119: ldc.i4.1 + IL_011a: call "void System.Console.Write(int)" + // sequence point: } + IL_011f: ret + } + """); + } + + [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] + public void Branchless_Negations() + { + var source = """ + using static System.Console; + + class C + { + static void Main() => M(1, 0, true); + static void M(int x, int y, bool b) + { + Write(!(x < y) ? 0 : 1); + Write(!!(x < y) ? 0 : 1); + Write(!!!(x < y) ? 0 : 1); + Write(!(x == y) ? 0 : 1); + Write(!b ? 0 : 1); + Write(!!b ? 0 : 1); + Write(!!!b ? 0 : 1); + Write(!false ? 0 : 1); + Write(!!false ? 0 : 1); + Write(!!!false ? 0 : 1); + } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.ReleaseExe, + expectedOutput: "0100101010"); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.M", """ + { + // Code size 85 (0x55) + .maxstack 2 + // sequence point: Write(!(x < y) ? 0 : 1); + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: clt + IL_0004: call "void System.Console.Write(int)" + // sequence point: Write(!!(x < y) ? 0 : 1); + IL_0009: ldarg.0 + IL_000a: ldarg.1 + IL_000b: clt + IL_000d: ldc.i4.0 + IL_000e: ceq + IL_0010: call "void System.Console.Write(int)" + // sequence point: Write(!!!(x < y) ? 0 : 1); + IL_0015: ldarg.0 + IL_0016: ldarg.1 + IL_0017: clt + IL_0019: call "void System.Console.Write(int)" + // sequence point: Write(!(x == y) ? 0 : 1); + IL_001e: ldarg.0 + IL_001f: ldarg.1 + IL_0020: ceq + IL_0022: call "void System.Console.Write(int)" + // sequence point: Write(!b ? 0 : 1); + IL_0027: ldarg.2 + IL_0028: ldc.i4.0 + IL_0029: cgt.un + IL_002b: call "void System.Console.Write(int)" + // sequence point: Write(!!b ? 0 : 1); + IL_0030: ldarg.2 + IL_0031: ldc.i4.0 + IL_0032: ceq + IL_0034: call "void System.Console.Write(int)" + // sequence point: Write(!!!b ? 0 : 1); + IL_0039: ldarg.2 + IL_003a: ldc.i4.0 + IL_003b: cgt.un + IL_003d: call "void System.Console.Write(int)" + // sequence point: Write(!false ? 0 : 1); + IL_0042: ldc.i4.0 + IL_0043: call "void System.Console.Write(int)" + // sequence point: Write(!!false ? 0 : 1); + IL_0048: ldc.i4.1 + IL_0049: call "void System.Console.Write(int)" + // sequence point: Write(!!!false ? 0 : 1); + IL_004e: ldc.i4.0 + IL_004f: call "void System.Console.Write(int)" + // sequence point: } + IL_0054: ret + } + """); + } + + [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] + public void Branchless_NonBinaryArms() + { + var source = """ + using static System.Console; + + class C + { + static void Main() => M(1, 0); + static void M(int x, int y) + { + Write(x == y ? 1 : 1); + Write(x != y ? 0 : 0); + Write(x <= y ? 0 : 2); + Write(x >= y ? 2 : 1); + Write(x < y ? 0 : -1); + Write(x < y ? 0d : 1d); + Write(x < y ? 0f : 1f); + Write(x < y ? 0m : 1m); + Write(x < y ? '\x0' : 'a'); + } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.ReleaseExe, + expectedOutput: "1022-1111a"); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.M", """ + { + // Code size 151 (0x97) + .maxstack 2 + // sequence point: Write(x == y ? 1 : 1); + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: beq.s IL_0007 + IL_0004: ldc.i4.1 + IL_0005: br.s IL_0008 + IL_0007: ldc.i4.1 + IL_0008: call "void System.Console.Write(int)" + // sequence point: Write(x != y ? 0 : 0); + IL_000d: ldarg.0 + IL_000e: ldarg.1 + IL_000f: bne.un.s IL_0014 + IL_0011: ldc.i4.0 + IL_0012: br.s IL_0015 + IL_0014: ldc.i4.0 + IL_0015: call "void System.Console.Write(int)" + // sequence point: Write(x <= y ? 0 : 2); + IL_001a: ldarg.0 + IL_001b: ldarg.1 + IL_001c: ble.s IL_0021 + IL_001e: ldc.i4.2 + IL_001f: br.s IL_0022 + IL_0021: ldc.i4.0 + IL_0022: call "void System.Console.Write(int)" + // sequence point: Write(x >= y ? 2 : 1); + IL_0027: ldarg.0 + IL_0028: ldarg.1 + IL_0029: bge.s IL_002e + IL_002b: ldc.i4.1 + IL_002c: br.s IL_002f + IL_002e: ldc.i4.2 + IL_002f: call "void System.Console.Write(int)" + // sequence point: Write(x < y ? 0 : -1); + IL_0034: ldarg.0 + IL_0035: ldarg.1 + IL_0036: blt.s IL_003b + IL_0038: ldc.i4.m1 + IL_0039: br.s IL_003c + IL_003b: ldc.i4.0 + IL_003c: call "void System.Console.Write(int)" + // sequence point: Write(x < y ? 0d : 1d); + IL_0041: ldarg.0 + IL_0042: ldarg.1 + IL_0043: blt.s IL_0050 + IL_0045: ldc.r8 1 + IL_004e: br.s IL_0059 + IL_0050: ldc.r8 0 + IL_0059: call "void System.Console.Write(double)" + // sequence point: Write(x < y ? 0f : 1f); + IL_005e: ldarg.0 + IL_005f: ldarg.1 + IL_0060: blt.s IL_0069 + IL_0062: ldc.r4 1 + IL_0067: br.s IL_006e + IL_0069: ldc.r4 0 + IL_006e: call "void System.Console.Write(float)" + // sequence point: Write(x < y ? 0m : 1m); + IL_0073: ldarg.0 + IL_0074: ldarg.1 + IL_0075: blt.s IL_007e + IL_0077: ldsfld "decimal decimal.One" + IL_007c: br.s IL_0083 + IL_007e: ldsfld "decimal decimal.Zero" + IL_0083: call "void System.Console.Write(decimal)" + // sequence point: Write(x < y ? '\x0' : 'a'); + IL_0088: ldarg.0 + IL_0089: ldarg.1 + IL_008a: blt.s IL_0090 + IL_008c: ldc.i4.s 97 + IL_008e: br.s IL_0091 + IL_0090: ldc.i4.0 + IL_0091: call "void System.Console.Write(char)" + // sequence point: } + IL_0096: ret + } + """); + } + + [Fact, WorkItem(61483, "https://github.com/dotnet/roslyn/issues/61483")] + public void Branchless_NonBinaryCondition() + { + // public static class C { public static bool M() => -1; } + var source1 = """ + .class public auto ansi abstract sealed beforefieldinit C + extends System.Object + { + .method public hidebysig static bool M () cil managed + { + .maxstack 8 + ldc.i4.m1 + ret + } + } + """; + var source2 = """ + System.Console.WriteLine(D.M1()); + System.Console.WriteLine(D.M2()); + + class D + { + public static int M1() => C.M() ? 1 : 0; + public static int M2() => C.M() ? 0 : 1; + } + """; + var comp = CreateCompilationWithIL(source2, source1, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: """ + 1 + 0 + """); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("D.M1", """ + { + // Code size 9 (0x9) + .maxstack 2 + // sequence point: C.M() ? 1 : 0 + IL_0000: call "bool C.M()" + IL_0005: ldc.i4.0 + IL_0006: cgt.un + IL_0008: ret + } + """); + verifier.VerifyMethodBody("D.M2", """ + { + // Code size 9 (0x9) + .maxstack 2 + // sequence point: C.M() ? 0 : 1 + IL_0000: call "bool C.M()" + IL_0005: ldc.i4.0 + IL_0006: ceq + IL_0008: ret + } + """); + } + [Fact, WorkItem(638289, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/638289")] public void ConditionalDelegateInterfaceUnification1() { @@ -1152,21 +1734,17 @@ static void Main() comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { - // Code size 25 (0x19) - .maxstack 2 + // Code size 19 (0x13) + .maxstack 3 IL_0000: ldc.i4.1 IL_0001: dup - IL_0002: brtrue.s IL_0007 - IL_0004: ldc.i4.1 - IL_0005: br.s IL_0008 - IL_0007: ldc.i4.0 - IL_0008: call ""void System.Console.Write(int)"" - IL_000d: brtrue.s IL_0012 - IL_000f: ldc.i4.1 - IL_0010: br.s IL_0013 - IL_0012: ldc.i4.0 - IL_0013: call ""void System.Console.Write(uint)"" - IL_0018: ret + IL_0002: ldc.i4.0 + IL_0003: ceq + IL_0005: call ""void System.Console.Write(int)"" + IL_000a: ldc.i4.0 + IL_000b: ceq + IL_000d: call ""void System.Console.Write(uint)"" + IL_0012: ret }"); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs index eab91bd63605e..caad6cb2b227c 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs @@ -5949,7 +5949,7 @@ .maxstack 2 compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); compVerifier.VerifyIL("C.M1", @" { - // Code size 35 (0x23) + // Code size 33 (0x21) .maxstack 2 .locals init (bool V_0) IL_0000: ldarg.0 @@ -5971,11 +5971,9 @@ .locals init (bool V_0) IL_001a: ldc.i4.0 IL_001b: stloc.0 IL_001c: ldloc.0 - IL_001d: brtrue.s IL_0021 - IL_001f: ldc.i4.0 + IL_001d: ldc.i4.0 + IL_001e: cgt.un IL_0020: ret - IL_0021: ldc.i4.1 - IL_0022: ret } "); compVerifier.VerifyIL("C.M2", @" diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs index b2808db170bf5..9460fa67eaeb2 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs @@ -563,7 +563,7 @@ public void TestProgrammaticSuppressionInfo_DiagnosticSuppressor() Assert.Single(diagnostics); var programmaticSuppression = diagnostics.Select(d => d.ProgrammaticSuppressionInfo).Single(); Assert.Equal(2, programmaticSuppression.Suppressions.Count); - var orderedSuppressions = Roslyn.Utilities.EnumerableExtensions.Order(programmaticSuppression.Suppressions).ToImmutableArrayOrEmpty(); + var orderedSuppressions = programmaticSuppression.Suppressions.Order().ToImmutableArrayOrEmpty(); Assert.Equal(suppressionId, orderedSuppressions[0].Id); Assert.Equal(suppressor.SuppressionDescriptor.Justification, orderedSuppressions[0].Justification); Assert.Equal(suppressionId2, orderedSuppressions[1].Id); diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/ManagedAddressTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/ManagedAddressTests.cs new file mode 100644 index 0000000000000..d0269fa467da2 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit2/Emit/ManagedAddressTests.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit +{ + public class ManagedAddressTests : CSharpTestBase + { + [Fact] + public void TestPointerToArray() + { + var source = """ + using System; + + unsafe + { + var x = new[] { 0, 1, 2 }; + int[]* xp = &x; + var c = new C(xp); + c.Print(); + } + + public unsafe class C + { + private int[]* _x; + + public C(int[]* x) + { + _x = x; + } + + public void Print() + { + int[] x = *_x; + for (int i = 0; i < x.Length; i++) + { + Console.Write(x[i]); + } + } + } + """; + + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe).VerifyDiagnostics( + // (6,5): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('int[]') + // int[]* xp = &x; + Diagnostic(ErrorCode.WRN_ManagedAddr, "int[]*").WithArguments("int[]").WithLocation(6, 5), + // (6,17): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('int[]') + // int[]* xp = &x; + Diagnostic(ErrorCode.WRN_ManagedAddr, "&x").WithArguments("int[]").WithLocation(6, 17), + // (13,20): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('int[]') + // private int[]* _x; + Diagnostic(ErrorCode.WRN_ManagedAddr, "_x").WithArguments("int[]").WithLocation(13, 20), + // (15,21): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('int[]') + // public C(int[]* x) + Diagnostic(ErrorCode.WRN_ManagedAddr, "x").WithArguments("int[]").WithLocation(15, 21) + ); + var verifier = CompileAndVerify(comp, expectedOutput: "012", verify: Verification.Fails with + { + ILVerifyMessage = """ + [
$]: Expected numeric type on the stack. { Offset = 0x12, Found = address of 'int32[]' } + [.ctor]: Unmanaged pointers are not a verifiable type. { Offset = 0x9 } + [Print]: Expected ByRef on the stack. { Offset = 0x7, Found = Native Int } + """, + PEVerifyMessage = """ + [ : Program::
$][offset 0x00000012][found address of ref ] Expected numeric type on the stack. + [ : C::.ctor][offset 0x00000009] Unmanaged pointers are not a verifiable type. + [ : C::Print][offset 0x00000007][found unmanaged pointer] Expected ByRef on the stack. + """, + }); + + verifier.VerifyMethodBody("", """ + { + // Code size 36 (0x24) + .maxstack 4 + .locals init (int[] V_0, //x + int[]* V_1, //xp + C V_2) //c + // sequence point: { + IL_0000: nop + // sequence point: var x = new[] { 0, 1, 2 }; + IL_0001: ldc.i4.3 + IL_0002: newarr "int" + IL_0007: dup + IL_0008: ldc.i4.1 + IL_0009: ldc.i4.1 + IL_000a: stelem.i4 + IL_000b: dup + IL_000c: ldc.i4.2 + IL_000d: ldc.i4.2 + IL_000e: stelem.i4 + IL_000f: stloc.0 + // sequence point: int[]* xp = &x; + IL_0010: ldloca.s V_0 + IL_0012: conv.u + IL_0013: stloc.1 + // sequence point: var c = new C(xp); + IL_0014: ldloc.1 + IL_0015: newobj "C..ctor(int[]*)" + IL_001a: stloc.2 + // sequence point: c.Print(); + IL_001b: ldloc.2 + IL_001c: callvirt "void C.Print()" + IL_0021: nop + // sequence point: } + IL_0022: nop + IL_0023: ret + } + """); + + verifier.VerifyMethodBody("C..ctor", """ + { + // Code size 16 (0x10) + .maxstack 2 + // sequence point: public C(int[]* x) + IL_0000: ldarg.0 + IL_0001: call "object..ctor()" + IL_0006: nop + // sequence point: { + IL_0007: nop + // sequence point: _x = x; + IL_0008: ldarg.0 + IL_0009: ldarg.1 + IL_000a: stfld "int[]* C._x" + // sequence point: } + IL_000f: ret + } + """); + + verifier.VerifyMethodBody("C.Print", """ + { + // Code size 39 (0x27) + .maxstack 2 + .locals init (int[] V_0, //x + int V_1, //i + bool V_2) + // sequence point: { + IL_0000: nop + // sequence point: int[] x = *_x; + IL_0001: ldarg.0 + IL_0002: ldfld "int[]* C._x" + IL_0007: ldind.ref + IL_0008: stloc.0 + // sequence point: int i = 0 + IL_0009: ldc.i4.0 + IL_000a: stloc.1 + // sequence point: + IL_000b: br.s IL_001c + // sequence point: { + IL_000d: nop + // sequence point: Console.Write(x[i]); + IL_000e: ldloc.0 + IL_000f: ldloc.1 + IL_0010: ldelem.i4 + IL_0011: call "void System.Console.Write(int)" + IL_0016: nop + // sequence point: } + IL_0017: nop + // sequence point: i++ + IL_0018: ldloc.1 + IL_0019: ldc.i4.1 + IL_001a: add + IL_001b: stloc.1 + // sequence point: i < x.Length + IL_001c: ldloc.1 + IL_001d: ldloc.0 + IL_001e: ldlen + IL_001f: conv.i4 + IL_0020: clt + IL_0022: stloc.2 + // sequence point: + IL_0023: ldloc.2 + IL_0024: brtrue.s IL_000d + // sequence point: } + IL_0026: ret + } + """); + } + } +} diff --git a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs index a6474e1d6859e..f718c080bade6 100644 --- a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs @@ -5912,5 +5912,31 @@ public static void Main() "; CreateCompilation(source).VerifyDiagnostics(); } + + [Fact] + public void NameOf_Nested() + { + var source = """ + System.Console.WriteLine(C.M()); + public class C + { + private C c; + public static string M() => nameof(c.c.c); + } + """; + + var expectedDiagnostic = + // (4,15): warning CS0649: Field 'C.c' is never assigned to, and will always have its default value null + // private C c; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "c").WithArguments("C.c", "null").WithLocation(4, 15); + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "c").VerifyDiagnostics(expectedDiagnostic); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "c").VerifyDiagnostics(expectedDiagnostic); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + expectedDiagnostic, + // (5,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(c.c.c); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "c").WithArguments("instance member in 'nameof'").WithLocation(5, 40)); + } } } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAwaitExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAwaitExpression.cs index 1a727046665cc..ab42b9a5bf259 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAwaitExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAwaitExpression.cs @@ -510,5 +510,35 @@ static async Task M(bool b, int i) "; VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67616")] + public void TestAwaitExpression_InStatement() + { + string source = @" +using System.Threading.Tasks; + +class C +{ + static async Task M() + { + /**/await M2()/**/; + } + + static Task M2() => throw null; +} +"; + string expectedOperationTree = @" +IAwaitOperation (OperationKind.Await, Type: System.String) (Syntax: 'await M2()') + Expression: + IInvocationOperation (System.Threading.Tasks.Task C.M2()) (OperationKind.Invocation, Type: System.Threading.Tasks.Task) (Syntax: 'M2()') + Instance Receiver: + null + Arguments(0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } } } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs index 2587442a62a18..4f7975c981bb2 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs @@ -223,5 +223,208 @@ void M() "; VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void NameOfFlow_InstanceMemberFromStatic_Flat() + { + var source = """ + public class C + { + public int Property { get; } + public int Field; + public event System.Action Event; + + public static string StaticMethod() + /**/{ + return nameof(Property) + + nameof(Field) + + nameof(Event); + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEvent") (Syntax: 'nameof(Prop ... meof(Event)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyField") (Syntax: 'nameof(Prop ... meof(Field)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Property") (Syntax: 'nameof(Property)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Field") (Syntax: 'nameof(Field)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Event") (Syntax: 'nameof(Event)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void NameOfFlow_InstanceMemberFromStatic_Flat_MethodGroup() + { + var source = """ + public class C + { + public void Method1() { } + public void Method1(int i) { } + public void Method2() { } + public static void Method2(int i) { } + + public static string StaticMethod() + /**/{ + return nameof(Method1) + + nameof(Method2); + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "Method1Method2") (Syntax: 'nameof(Meth ... of(Method2)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method1") (Syntax: 'nameof(Method1)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method2") (Syntax: 'nameof(Method2)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void NameOfFlow_InstanceMemberFromStatic_Nested() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + + public static string StaticMethod() + /**/{ + return nameof(Property.Property) + + nameof(Property.Field) + + nameof(Property.Event) + + nameof(Field.Property) + + nameof(Field.Field) + + nameof(Field.Event); + }/**/ + } + + public class C1 + { + public int Property { get; } + public int Field; + public event System.Action Event; + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEventPropertyFieldEvent") (Syntax: 'nameof(Prop ... ield.Event)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEventPropertyField") (Syntax: 'nameof(Prop ... ield.Field)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEventProperty") (Syntax: 'nameof(Prop ... d.Property)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEvent") (Syntax: 'nameof(Prop ... erty.Event)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyField") (Syntax: 'nameof(Prop ... erty.Field)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Property") (Syntax: 'nameof(Prop ... y.Property)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Field") (Syntax: 'nameof(Property.Field)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Event") (Syntax: 'nameof(Property.Event)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Property") (Syntax: 'nameof(Field.Property)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Field") (Syntax: 'nameof(Field.Field)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Event") (Syntax: 'nameof(Field.Event)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void NameOfFlow_InstanceMemberFromStatic_Nested_MethodGroup() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + public event System.Action Event; + + public static string StaticMethod() + /**/{ + return nameof(Property.Method) + + nameof(Field.Method) + + nameof(Event.Invoke); + }/**/ + } + + public class C1 + { + public void Method() { } + public void Method(int i) { } + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "MethodMethodInvoke") (Syntax: 'nameof(Prop ... ent.Invoke)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "MethodMethod") (Syntax: 'nameof(Prop ... eld.Method)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method") (Syntax: 'nameof(Property.Method)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method") (Syntax: 'nameof(Field.Method)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Invoke") (Syntax: 'nameof(Event.Invoke)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 4699165bab2b7..98a9d1f8137a9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -4,13 +4,15 @@ #nullable disable -using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -using System.Threading; -using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -954,6 +956,315 @@ private static void Use(object o) {} Assert.Equal(0, symbolInfo.CandidateSymbols.Length); } + [Fact] + public void SymbolInfo_InstanceMemberFromStatic_Flat() + { + var source = """ + public class C + { + public int Property { get; } + public int Field; + public event System.Action Event; + + public static string StaticField = + nameof(Property) + + nameof(Field) + + nameof(Event); + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var cProperty = comp.GetMember("C.Property"); + var cField = comp.GetMember("C.Field"); + var cEvent = comp.GetMember("C.Event"); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(3, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(3, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Property", cProperty); + verify(1, "Field", cField); + verify(2, "Event", cEvent); + + void verify(int index, string expression, Symbol symbol) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(SymbolInfo symbolInfo) + { + Assert.NotNull(symbolInfo.Symbol); + Assert.Same(symbol.GetPublicSymbol(), symbolInfo.Symbol); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + + [Fact] + public void SymbolInfo_InstanceMemberFromStatic_Flat_MethodGroup() + { + var source = """ + public class C + { + public void Method1() { } + public void Method1(int i) { } + public void Method2() { } + public static void Method2(int i) { } + + public static string StaticField = + nameof(Method1) + + nameof(Method2); + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var cMethods1 = comp.GetMembers("C.Method1"); + Assert.Equal(2, cMethods1.Length); + var cMethods2 = comp.GetMembers("C.Method2"); + Assert.Equal(2, cMethods2.Length); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(2, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(2, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Method1", cMethods1); + verify(1, "Method2", cMethods2); + + void verify(int index, string expression, ImmutableArray symbols) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(CandidateReason.MemberGroup, model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(CandidateReason.MemberGroup, model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(CandidateReason.OverloadResolutionFailure, model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(CandidateReason reason, SymbolInfo symbolInfo) + { + Assert.Equal(reason, symbolInfo.CandidateReason); + AssertEx.SetEqual( + symbols.Select(s => s.GetPublicSymbol()), + symbolInfo.CandidateSymbols, + Roslyn.Utilities.ReferenceEqualityComparer.Instance); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void SymbolInfo_InstanceMemberFromStatic_Nested() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + + public static string StaticField = + nameof(Property.Property) + + nameof(Property.Field) + + nameof(Property.Event) + + nameof(Field.Property) + + nameof(Field.Field) + + nameof(Field.Event); + } + + public class C1 + { + public int Property { get; } + public int Field; + public event System.Action Event; + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var c1Property = comp.GetMember("C1.Property"); + var c1Field = comp.GetMember("C1.Field"); + var c1Event = comp.GetMember("C1.Event"); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(6, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(6, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Property.Property", c1Property); + verify(1, "Property.Field", c1Field); + verify(2, "Property.Event", c1Event); + verify(3, "Field.Property", c1Property); + verify(4, "Field.Field", c1Field); + verify(5, "Field.Event", c1Event); + + void verify(int index, string expression, Symbol symbol) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(SymbolInfo symbolInfo) + { + Assert.NotNull(symbolInfo.Symbol); + Assert.Same(symbol.GetPublicSymbol(), symbolInfo.Symbol); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void SymbolInfo_InstanceMemberFromStatic_Nested_MethodGroup() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + public event System.Action Event; + + public static string StaticField = + nameof(Property.Method) + + nameof(Field.Method) + + nameof(Event.Invoke); + } + + public class C1 + { + public void Method() { } + public void Method(int i) { } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var c1Methods = comp.GetMembers("C1.Method").ToArray(); + Assert.Equal(2, c1Methods.Length); + var c1Event = comp.GetMember("C1.Event"); + var actionInvoke = comp.GetWellKnownType(WellKnownType.System_Action).GetMember("Invoke"); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(3, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(3, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Property.Method", c1Methods); + verify(1, "Field.Method", c1Methods); + verify(2, "Event.Invoke", actionInvoke); + + void verify(int index, string expression, params Symbol[] symbols) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(CandidateReason.MemberGroup, model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(CandidateReason.MemberGroup, model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(CandidateReason.OverloadResolutionFailure, model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(CandidateReason reason, SymbolInfo symbolInfo) + { + Assert.Equal(reason, symbolInfo.CandidateReason); + AssertEx.SetEqual( + symbols.Select(s => s.GetPublicSymbol()), + symbolInfo.CandidateSymbols, + Roslyn.Utilities.ReferenceEqualityComparer.Instance); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + [Fact] public void ExtensionMethodConstraintFailed() { @@ -1487,5 +1798,580 @@ public void nameof(string x) var option = TestOptions.ReleaseDll; CreateCompilation(source, options: option).VerifyDiagnostics(); } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromStaticMemberInNameof_Flat() + { + var source = @" +System.Console.Write(C.M()); +public class C +{ + public object Property { get; } + public object Field; + public event System.Action Event; + public void M2() { } + public static string M() => nameof(Property) + + "","" + nameof(Field) + + "","" + nameof(Event) + + "","" + nameof(M2) + ; +}"; + var expectedOutput = "Property,Field,Event,M2"; + + CompileAndVerify(source, parseOptions: TestOptions.Regular11, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromStaticMemberInNameof_Nested() + { + var source = @" +System.Console.Write(C.M()); +public class C +{ + public C1 Property { get; } + public C1 Field; + public event System.Action Event; + public static string M() => nameof(Property.Property) + + "","" + nameof(Property.Field) + + "","" + nameof(Property.Method) + + "","" + nameof(Property.Event) + + "","" + nameof(Field.Property) + + "","" + nameof(Field.Field) + + "","" + nameof(Field.Method) + + "","" + nameof(Field.Event) + + "","" + nameof(Event.Invoke) + ; +} + +public class C1 +{ + public int Property { get; } + public int Field; + public void Method(){} + public event System.Action Event; +}"; + var expectedOutput = "Property,Field,Method,Event,Property,Field,Method,Event,Invoke"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (8,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Property.Property) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(8, 40), + // (9,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Property.Field) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(9, 24), + // (10,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Property.Method) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(10, 24), + // (11,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Property.Event) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(11, 24), + // (12,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Property) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(12, 24), + // (13,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Field) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(13, 24), + // (14,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Method) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(14, 24), + // (15,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Event) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(15, 24), + // (16,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Event.Invoke) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(16, 24)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void InstanceFromStatic_Lambdas() + { + var source = """ + using System; + Console.Write(C.Names()); + public class C + { + public object Property { get; } + public object Field; + public event Action Event; + public void Method() { } + public static string Names() + { + var lambda1 = static () => nameof(Property); + var lambda2 = static (string f = nameof(Field)) => f; + var lambda3 = static () => nameof(Event.Invoke); + var lambda4 = static (string i = nameof(Event.Invoke)) => i; + return lambda1() + "," + lambda2() + "," + lambda3() + "," + lambda4(); + } + } + """; + var expectedOutput = "Property,Field,Invoke,Invoke"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (12,40): error CS8652: The feature 'lambda optional parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda2 = static (string f = nameof(Field)) => f; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "=").WithArguments("lambda optional parameters").WithLocation(12, 40), + // (13,43): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda3 = static () => nameof(Event.Invoke); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(13, 43), + // (14,40): error CS8652: The feature 'lambda optional parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda4 = static (string i = nameof(Event.Invoke)) => i; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "=").WithArguments("lambda optional parameters").WithLocation(14, 40), + // (14,49): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda4 = static (string i = nameof(Event.Invoke)) => i; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(14, 49)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void InstanceFromStatic_LocalFunctions() + { + var source = """ + using System; + Console.Write(C.Names()); + public class C + { + public object Property { get; } + public object Field; + public event Action Event; + public void Method() { } + public static string Names() + { + static string local1() => nameof(Property); + static string local2(string f = nameof(Field)) => f; + static string local3() => nameof(Event.Invoke); + static string local4(string i = nameof(Event.Invoke)) => i; + return local1() + "," + local2() + "," + local3() + "," + local4(); + } + } + """; + var expectedOutput = "Property,Field,Invoke,Invoke"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (13,42): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static string local3() => nameof(Event.Invoke); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(13, 42), + // (14,48): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static string local4(string i = nameof(Event.Invoke)) => i; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(14, 48)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromFieldInitializerInNameof() + { + var source = @" +System.Console.Write(new C().S); +public class C +{ + public string S { get; } = nameof(S.Length); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,39): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string S { get; } = nameof(S.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 39)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromAttributeInNameof() + { + var source = @" +var p = new C().P; // 1 +public class C +{ + [System.Obsolete(nameof(S.Length))] + public int P { get; } + public string S { get; } +}"; + var expectedDiagnostics = new[] + { + // (2,9): warning CS0618: 'C.P' is obsolete: 'Length' + // var p = new C().P; // 1 + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "new C().P").WithArguments("C.P", "Length").WithLocation(2, 9) + }; + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,29): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [System.Obsolete(nameof(S.Length))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 29)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromConstructorInitializersInNameof() + { + var source = @" +System.Console.WriteLine(new C().S); +public class C +{ + public C(string s){ S = s; } + public C() : this(nameof(S.Length)){} + public string S { get; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (6,30): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public C() : this(nameof(S.Length)){} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(6, 30)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanAccessStructInstancePropertyInLambdaInNameof() + { + var source = @" +using System; + +string s = ""str""; +new S().M(ref s); + +public struct S +{ + public string P { get; } + public void M(ref string x) + { + Func func = () => nameof(P.Length); + Console.WriteLine(func()); + } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (12,42): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func func = () => nameof(P.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "P").WithArguments("instance member in 'nameof'").WithLocation(12, 42)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof1() + { + var source = @" +System.Console.WriteLine(new C().M()); +public class C +{ + public C Prop { get; } + public static int StaticProp { get; } + public string M() => nameof(Prop.StaticProp); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticProp").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticProp").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,33): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.StaticProp").WithArguments("instance member in 'nameof'").WithLocation(7, 33)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof2() + { + var source = @" +System.Console.WriteLine(C.M()); +public class C +{ + public C Prop { get; } + public static int StaticProp { get; } + public static string M() => nameof(Prop.StaticProp); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticProp").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticProp").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop").WithArguments("instance member in 'nameof'").WithLocation(7, 40), + // (7,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.StaticProp").WithArguments("instance member in 'nameof'").WithLocation(7, 40)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof3() + { + var source = @" +System.Console.WriteLine(C.M()); +public class C +{ + public C Prop { get; } + public static string M() => nameof(Prop.M); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "M").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "M").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (6,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Prop.M); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop").WithArguments("instance member in 'nameof'").WithLocation(6, 40)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof4() + { + var source = @" +System.Console.WriteLine(new C().M()); +public class C +{ + public C Prop { get; } + public static void StaticMethod(){} + public string M() => nameof(Prop.StaticMethod); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromStaticMemberInNameofInCSharp11() + { + var source = @" +public class C +{ + public string S { get; } + public static string M() => nameof(S.Length); +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(S.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 40)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromFieldInitializerInNameofInCSharp11() + { + var source = @" +public class C +{ + public string S { get; } = nameof(S.Length); +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (4,39): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string S { get; } = nameof(S.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(4, 39)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromAttributeInNameofInCSharp11() + { + var source = @" +public class C +{ + [System.Obsolete(nameof(S.Length))] + public int P { get; } + public string S { get; } +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (4,29): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [System.Obsolete(nameof(S.Length))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(4, 29)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromConstructorInitializersInNameofInCSharp11() + { + var source = @" +public class C +{ + public C(string s){} + public C() : this(nameof(S.Length)){} + public string S { get; } +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,30): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public C() : this(nameof(S.Length)){} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 30)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotAccessStructInstancePropertyInLambdaInNameofInCSharp11() + { + var source = @" +using System; + +public struct S +{ + public string P { get; } + public void M(ref string x) + { + Func func = () => nameof(P.Length); + } +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (9,42): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func func = () => nameof(P.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "P").WithArguments("instance member in 'nameof'").WithLocation(9, 42)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceStaticPropertyFromInstanceMemberInNameofInCSharp11() + { + var source = @" +public class C +{ + public C Prop { get; } + public static int StaticProp { get; } + public string M() => nameof(Prop.StaticProp); +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (6,33): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.StaticProp").WithArguments("instance member in 'nameof'").WithLocation(6, 33)); + } + + [Fact] + public void TestCanReferenceStaticMethodFromInstanceMemberInNameofInCSharp11() + { + var source = @" +System.Console.WriteLine(new C().M()); +public class C +{ + public C Prop { get; } + public static void StaticMethod(){} + public string M() => nameof(Prop.StaticMethod); +}"; + CompileAndVerify(source, parseOptions: TestOptions.Regular11, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticMethod").VerifyDiagnostics(); + } + + [Fact] + public void TestCanAccessRefParameterInLambdaInNameof() + { + var source = @" +using System; + +string s = ""str""; +new S().M(ref s); + +public struct S +{ + public void M(ref string x) + { + Func func = () => nameof(x.Length); + Console.WriteLine(func()); + } +}"; + CompileAndVerify(source, parseOptions: TestOptions.Regular11, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameofUsedRecursivelyInAttributes1() + { + var source = @" +using System; +using System.Reflection; +Console.WriteLine(typeof(C).GetProperty(""Prop"").GetCustomAttribute().S); +class C +{ + [Attr(nameof(Prop.StaticMethod))] + public C Prop { get; } + public static void StaticMethod(){} +} +class Attr : Attribute +{ + public readonly string S; + public Attr(string s) { S = s; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,18): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [Attr(nameof(Prop.StaticMethod))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop").WithArguments("instance member in 'nameof'").WithLocation(7, 18)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameofUsedRecursivelyInAttributes2() + { + var source = @" +using System; +using System.Reflection; +Console.WriteLine(typeof(C).GetProperty(""Prop"").GetCustomAttribute().S); +class C +{ + [Attr(nameof(Prop.Prop))] + public static C Prop { get; } +} +class Attr : Attribute +{ + public readonly string S; + public Attr(string s) { S = s; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Prop").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Prop").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,18): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [Attr(nameof(Prop.Prop))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.Prop").WithArguments("instance member in 'nameof'").WithLocation(7, 18)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameofUsedRecursivelyInAttributes3() + { + var source = @" +using System; +using System.Reflection; +Console.WriteLine(typeof(C).GetCustomAttribute().S); +[Attr(nameof(C.Prop.Prop))] +class C +{ + public static C Prop { get; } +} +class Attr : Attribute +{ + public readonly string S; + public Attr(string s) { S = s; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Prop").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Prop").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,14): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [Attr(nameof(C.Prop.Prop))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "C.Prop.Prop").WithArguments("instance member in 'nameof'").WithLocation(5, 14)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestInvalidRecursiveUsageOfNameofInAttributesDoesNotCrashCompiler1() + { + var source = @" +class C +{ + [Attr(nameof(Method().Method))] + T Method() where T : C => default; +} +class Attr : System.Attribute { public Attr(string s) {} }"; + var expectedDiagnostics = new[] + { + // (4,18): error CS0411: The type arguments for method 'C.Method()' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // [Attr(nameof(Method().Method))] + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Method").WithArguments("C.Method()").WithLocation(4, 18) + }; + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestInvalidRecursiveUsageOfNameofInAttributesDoesNotCrashCompiler2() + { + var source = @" +class C +{ + [Attr(nameof(Method().Method))] + T Method() where T : C => default; +} +class Attr : System.Attribute { public Attr(string s) {} }"; + var expectedDiagnostics = new[] + { + // (4,18): error CS8082: Sub-expression cannot be used in an argument to nameof. + // [Attr(nameof(Method().Method))] + Diagnostic(ErrorCode.ERR_SubexpressionNotInNameof, "Method()").WithLocation(4, 18) + }; + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 982875722db70..33734de9ae300 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -156945,5 +156945,113 @@ public void Repro66960_2() Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(5, 54) ); } + + [WorkItem(67040, "https://github.com/dotnet/roslyn/issues/67040")] + [Fact] + public void NullCoalescingAssignment_NullableValueTypeWithValue_01() + { + var source = """ + #pragma warning disable 649 + #nullable enable + readonly struct S + { + public readonly T Item; + } + class Program + { + static void M(S x, bool b) + { + S? y; + y = x; + y ??= b ? default : default; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void NullCoalescingAssignment_NullableValueTypeWithValue_02() + { + var source = """ + #pragma warning disable 649 + #nullable enable + readonly struct S + { + public readonly T Item; + } + class Program + { + static void M(S x) + { + S? y = x; + S? z = null; + z ??= y; + z.Value.Item.ToString(); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void NullCoalescingAssignment_NullableValueTypeWithValue_03() + { + var source = """ + #nullable enable + struct S + { + public object? Item; + } + class Program + { + static void M() + { + S x = new S(); + x.Item = 1; + S? y; + y = x; + y ??= new S(); + y.Value.Item.ToString(); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (15,9): warning CS8602: Dereference of a possibly null reference. + // y.Value.Item.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y.Value.Item").WithLocation(15, 9)); + } + + [Fact] + public void NullCoalescingAssignment_NullableValueTypeWithValue_04() + { + var source = """ + #nullable enable + interface I + { + object? Item { get; set; } + } + class Program + { + static void M() where T : struct, I + { + T x = new T(); + x.Item = 1; + T? y; + y = x; + y ??= new T(); + y.Value.Item.ToString(); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (15,9): warning CS8602: Dereference of a possibly null reference. + // y.Value.Item.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y.Value.Item").WithLocation(15, 9)); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs index 61075f1fb3320..764b90baddebe 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs @@ -3510,7 +3510,7 @@ where node.IsKind(SyntaxKind.CollectionInitializerExpression) Assert.Equal(2, symbolInfo.CandidateSymbols.Length); Assert.Equal(new[] {"void X.Add(System.Collections.Generic.List x)", "void X.Add(X x)"}, - Roslyn.Utilities.EnumerableExtensions.Order(symbolInfo.CandidateSymbols.Select(s => s.ToTestDisplayString())).ToArray()); + symbolInfo.CandidateSymbols.Select(s => s.ToTestDisplayString()).Order().ToArray()); } [WorkItem(529787, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529787")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 922e4f0151398..dae6fd47c58ab 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -3303,7 +3303,7 @@ public class C where T : System.TypedReference } [Fact] - public void ERR_ManagedAddr_ShallowRecursive() + public void WRN_ManagedAddr_ShallowRecursive() { var text = @" public unsafe struct S1 @@ -3325,7 +3325,7 @@ public unsafe struct S2 } [Fact] - public void ERR_ManagedAddr_DeepRecursive() + public void WRN_ManagedAddr_DeepRecursive() { var text = @" public unsafe struct A @@ -3357,7 +3357,7 @@ public struct C } [Fact] - public void ERR_ManagedAddr_Alias() + public void WRN_ManagedAddr_Alias() { var text = @" using Alias = S; @@ -3375,7 +3375,7 @@ public unsafe struct S } [Fact()] - public void ERR_ManagedAddr_Members() + public void WRN_ManagedAddr_Members() { var text = @" public unsafe struct S @@ -3413,6 +3413,21 @@ void M(S* p) { } Diagnostic(ErrorCode.WRN_ManagedAddr, "p").WithArguments("S").WithLocation(10, 17)); } + [Fact] + public void WRN_ManagedAddr_ArrayPointer() + { + var text = @" +public unsafe struct S +{ + public int[]* s; +} +"; + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (4,19): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('int[]') + // public int[]* s; + Diagnostic(ErrorCode.WRN_ManagedAddr, "s").WithArguments("int[]").WithLocation(4, 19)); + } + [WorkItem(10195, "https://github.com/dotnet/roslyn/issues/10195")] [Fact] public void PointerToStructInPartialMethodSignature() diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index f35bd414f7716..fe6c672902c0a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -13102,18 +13102,6 @@ static void MT2() where T : I1 targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (14,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P01").WithArguments("I1.P01").WithLocation(14, 20), - // (15,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 20), - // (28,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P01").WithArguments("I1.P01").WithLocation(28, 20), - // (30,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P04").WithArguments("I1.P04").WithLocation(30, 20), // (35,20): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter // _ = nameof(T.P03); Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(35, 20), @@ -13984,18 +13972,6 @@ static void MT2() where T : I1 targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (14,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P01").WithArguments("I1.P01").WithLocation(14, 20), - // (15,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 20), - // (28,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P01").WithArguments("I1.P01").WithLocation(28, 20), - // (30,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P04").WithArguments("I1.P04").WithLocation(30, 20), // (35,20): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter // _ = nameof(T.P03); Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(35, 20), diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs index 0ce1eb14cadff..e7d0e088e2699 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs @@ -1404,6 +1404,79 @@ public void NullableReturnTypeOrConditional_17() EOF(); } + [Fact] + public void NullablePointer() + { + UsingExpression("int?* () => default"); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.PointerType); + { + N(SyntaxKind.NullableType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.QuestionToken); + } + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + EOF(); + } + + [Fact] + public void ArrayPointer() + { + UsingExpression("int[]* () => default"); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.PointerType); + { + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.AsteriskToken); + } + + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.DefaultLiteralExpression); + { + N(SyntaxKind.DefaultKeyword); + } + } + EOF(); + } + [Fact] public void NullableReturnTypeOrConditional_18() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs index cbd4db8d3850a..29899976828fa 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs @@ -2155,32 +2155,20 @@ public void BrokenPattern_06() // (1,30): error CS0103: The name 'e' does not exist in the current context // class C { void M() { switch (e) { case (: ; } } } Diagnostic(ErrorCode.ERR_NameNotInContext, "e").WithArguments("e").WithLocation(1, 30), - // (1,35): error CS8070: Control cannot fall out of switch from final case label ('case (: ') + // (1,35): error CS8070: Control cannot fall out of switch from final case label ('case (:') // class C { void M() { switch (e) { case (: ; } } } - Diagnostic(ErrorCode.ERR_SwitchFallOut, "case (: ").WithArguments("case (: ").WithLocation(1, 35), + Diagnostic(ErrorCode.ERR_SwitchFallOut, "case (:").WithArguments("case (:").WithLocation(1, 35), // (1,40): error CS8370: Feature 'recursive patterns' is not available in C# 7.3. Please use language version 8.0 or greater. // class C { void M() { switch (e) { case (: ; } } } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "(: ").WithArguments("recursive patterns", "8.0").WithLocation(1, 40), - // (1,41): error CS1001: Identifier expected + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "(").WithArguments("recursive patterns", "8.0").WithLocation(1, 40), + // (1,41): error CS1026: ) expected // class C { void M() { switch (e) { case (: ; } } } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ":").WithLocation(1, 41), - // (1,43): error CS1026: ) expected - // class C { void M() { switch (e) { case (: ; } } } - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 43), - // (1,43): error CS1003: Syntax error, ':' expected - // class C { void M() { switch (e) { case (: ; } } } - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(":").WithLocation(1, 43)); + Diagnostic(ErrorCode.ERR_CloseParenExpected, ":").WithLocation(1, 41)); UsingStatement(test, TestOptions.RegularWithoutRecursivePatterns, - // (1,20): error CS1001: Identifier expected - // switch (e) { case (: ; } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ":").WithLocation(1, 20), - // (1,22): error CS1026: ) expected - // switch (e) { case (: ; } - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 22), - // (1,22): error CS1003: Syntax error, ':' expected + // (1,20): error CS1026: ) expected // switch (e) { case (: ; } - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(":").WithLocation(1, 22)); + Diagnostic(ErrorCode.ERR_CloseParenExpected, ":").WithLocation(1, 20)); N(SyntaxKind.SwitchStatement); { N(SyntaxKind.SwitchKeyword); @@ -2204,7 +2192,7 @@ public void BrokenPattern_06() M(SyntaxKind.CloseParenToken); } } - M(SyntaxKind.ColonToken); + N(SyntaxKind.ColonToken); } N(SyntaxKind.EmptyStatement); { @@ -2737,25 +2725,16 @@ public void BrokenRecursivePattern01() Diagnostic(ErrorCode.ERR_NameNotInContext, "e").WithArguments("e").WithLocation(1, 30), // (1,40): error CS8370: Feature 'recursive patterns' is not available in C# 7.3. Please use language version 8.0 or greater. // class C { void M() { switch (e) { case T( : Q x = n; break; } } } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "T( : Q x ").WithArguments("recursive patterns", "8.0").WithLocation(1, 40), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "T( ").WithArguments("recursive patterns", "8.0").WithLocation(1, 40), // (1,40): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) // class C { void M() { switch (e) { case T( : Q x = n; break; } } } Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(1, 40), - // (1,43): error CS1001: Identifier expected - // class C { void M() { switch (e) { case T( : Q x = n; break; } } } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ":").WithLocation(1, 43), // (1,45): error CS0246: The type or namespace name 'Q' could not be found (are you missing a using directive or an assembly reference?) // class C { void M() { switch (e) { case T( : Q x = n; break; } } } Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Q").WithArguments("Q").WithLocation(1, 45), - // (1,49): error CS1026: ) expected - // class C { void M() { switch (e) { case T( : Q x = n; break; } } } - Diagnostic(ErrorCode.ERR_CloseParenExpected, "=").WithLocation(1, 49), - // (1,49): error CS1003: Syntax error, ':' expected - // class C { void M() { switch (e) { case T( : Q x = n; break; } } } - Diagnostic(ErrorCode.ERR_SyntaxError, "=").WithArguments(":").WithLocation(1, 49), - // (1,49): error CS1525: Invalid expression term '=' + // (1,43): error CS1026: ) expected // class C { void M() { switch (e) { case T( : Q x = n; break; } } } - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(1, 49), + Diagnostic(ErrorCode.ERR_CloseParenExpected, ":").WithLocation(1, 43), // (1,51): error CS0103: The name 'n' does not exist in the current context // class C { void M() { switch (e) { case T( : Q x = n; break; } } } Diagnostic(ErrorCode.ERR_NameNotInContext, "n").WithArguments("n").WithLocation(1, 51)); @@ -2763,21 +2742,9 @@ public void BrokenRecursivePattern01() // This put the parser into an infinite loop at one time. The precise diagnostics and nodes // are not as important as the fact that it terminates. UsingStatement(test, TestOptions.RegularWithoutRecursivePatterns, - // (1,22): error CS1001: Identifier expected - // switch (e) { case T( : Q x = n; break; } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ":").WithLocation(1, 22), - // (1,28): error CS1003: Syntax error, ',' expected - // switch (e) { case T( : Q x = n; break; } - Diagnostic(ErrorCode.ERR_SyntaxError, "=").WithArguments(",").WithLocation(1, 28), - // (1,30): error CS1003: Syntax error, ',' expected - // switch (e) { case T( : Q x = n; break; } - Diagnostic(ErrorCode.ERR_SyntaxError, "n").WithArguments(",").WithLocation(1, 30), - // (1,31): error CS1026: ) expected - // switch (e) { case T( : Q x = n; break; } - Diagnostic(ErrorCode.ERR_CloseParenExpected, ";").WithLocation(1, 31), - // (1,31): error CS1003: Syntax error, ':' expected + // (1,22): error CS1026: ) expected // switch (e) { case T( : Q x = n; break; } - Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(":").WithLocation(1, 31) + Diagnostic(ErrorCode.ERR_CloseParenExpected, ":").WithLocation(1, 22) ); N(SyntaxKind.SwitchStatement); { @@ -2803,38 +2770,32 @@ public void BrokenRecursivePattern01() N(SyntaxKind.PositionalPatternClause); { N(SyntaxKind.OpenParenToken); - N(SyntaxKind.Subpattern); - { - N(SyntaxKind.DeclarationPattern); - { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "Q"); - } - N(SyntaxKind.SingleVariableDesignation); - { - N(SyntaxKind.IdentifierToken, "x"); - } - } - } - M(SyntaxKind.CommaToken); - N(SyntaxKind.Subpattern); + M(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Q"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + N(SyntaxKind.EqualsValueClause); { - N(SyntaxKind.ConstantPattern); + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "n"); - } + N(SyntaxKind.IdentifierToken, "n"); } } - M(SyntaxKind.CloseParenToken); } } - M(SyntaxKind.ColonToken); - } - N(SyntaxKind.EmptyStatement); - { N(SyntaxKind.SemicolonToken); } N(SyntaxKind.BreakStatement); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index 91436d557a138..448aca9a97469 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -4178,37 +4178,64 @@ public void ParseCreateNullableTuple_02() [Fact] public void ParsePointerToArray() { - UsingStatement("int []* p;", - // (1,7): error CS1001: Identifier expected - // int []* p; - Diagnostic(ErrorCode.ERR_IdentifierExpected, "*").WithLocation(1, 7), - // (1,7): error CS1003: Syntax error, ',' expected - // int []* p; - Diagnostic(ErrorCode.ERR_SyntaxError, "*").WithArguments(",").WithLocation(1, 7) - ); + UsingStatement("int []* p;"); N(SyntaxKind.LocalDeclarationStatement); { N(SyntaxKind.VariableDeclaration); { - N(SyntaxKind.ArrayType); + N(SyntaxKind.PointerType); { - N(SyntaxKind.PredefinedType); + N(SyntaxKind.ArrayType); { - N(SyntaxKind.IntKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } } - N(SyntaxKind.ArrayRankSpecifier); + N(SyntaxKind.AsteriskToken); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "p"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void ParsePointerToNullableType() + { + UsingStatement("int?* p;"); + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PointerType); + { + N(SyntaxKind.NullableType); { - N(SyntaxKind.OpenBracketToken); - N(SyntaxKind.OmittedArraySizeExpression); + N(SyntaxKind.PredefinedType); { - N(SyntaxKind.OmittedArraySizeExpressionToken); + N(SyntaxKind.IntKeyword); } - N(SyntaxKind.CloseBracketToken); + N(SyntaxKind.QuestionToken); } + N(SyntaxKind.AsteriskToken); } - M(SyntaxKind.VariableDeclarator); + N(SyntaxKind.VariableDeclarator); { - M(SyntaxKind.IdentifierToken); + N(SyntaxKind.IdentifierToken, "p"); } } N(SyntaxKind.SemicolonToken); @@ -4737,6 +4764,640 @@ void M() EOF(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67757")] + public void ParseSwitchStatementWithUnclosedRecursivePattern1() + { + UsingStatement(""" + switch (obj) + { + case Type { Prop: Type { }: + case Type { Prop: Type { }: + break; + } + """, + // (3,31): error CS1513: } expected + // case Type { Prop: Type { }: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 31), + // (4,31): error CS1513: } expected + // case Type { Prop: Type { }: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(4, 31) + ); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67757")] + public void ParseSwitchStatementWithUnclosedRecursivePattern2() + { + UsingStatement(""" + switch (obj) + { + case Type { Prop: Type {: + case Type { Prop: Type {: + break; + } + """, + // (3,29): error CS1513: } expected + // case Type { Prop: Type {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 29), + // (3,29): error CS1513: } expected + // case Type { Prop: Type {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 29), + // (4,29): error CS1513: } expected + // case Type { Prop: Type {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(4, 29), + // (4,29): error CS1513: } expected + // case Type { Prop: Type {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(4, 29) + ); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67757")] + public void ParseSwitchStatementWithUnclosedRecursivePattern3() + { + UsingStatement(""" + switch (obj) + { + case { Prop: { Prop: {: + case { Prop: { Prop: {: + break; + } + """, + // (3,27): error CS1513: } expected + // case { Prop: { Prop: {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 27), + // (3,27): error CS1513: } expected + // case { Prop: { Prop: {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 27), + // (3,27): error CS1513: } expected + // case { Prop: { Prop: {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 27), + // (4,27): error CS1513: } expected + // case { Prop: { Prop: {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(4, 27), + // (4,27): error CS1513: } expected + // case { Prop: { Prop: {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(4, 27), + // (4,27): error CS1513: } expected + // case { Prop: { Prop: {: + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(4, 27) + ); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.Subpattern); + { + N(SyntaxKind.NameColon); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PropertyPatternClause); + { + N(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + } + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67757")] + public void ParseSwitchStatementWithUnclosedListPattern1() + { + UsingStatement(""" + switch (obj) + { + case [: + case [: + break; + } + """, + // (3,11): error CS1003: Syntax error, ']' expected + // case [: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(3, 11), + // (4,11): error CS1003: Syntax error, ']' expected + // case [: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(4, 11) + ); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67757")] + public void ParseSwitchStatementWithUnclosedListPattern2() + { + UsingStatement(""" + switch (obj) + { + case [[: + case [[: + break; + } + """, + // (3,12): error CS1003: Syntax error, ']' expected + // case [[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(3, 12), + // (3,12): error CS1003: Syntax error, ']' expected + // case [[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(3, 12), + // (4,12): error CS1003: Syntax error, ']' expected + // case [[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(4, 12), + // (4,12): error CS1003: Syntax error, ']' expected + // case [[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(4, 12) + ); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67757")] + public void ParseSwitchStatementWithUnclosedListPattern3() + { + UsingStatement(""" + switch (obj) + { + case [[[: + case [[[: + break; + } + """, + // (3,13): error CS1003: Syntax error, ']' expected + // case [[[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(3, 13), + // (3,13): error CS1003: Syntax error, ']' expected + // case [[[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(3, 13), + // (3,13): error CS1003: Syntax error, ']' expected + // case [[[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(3, 13), + // (4,13): error CS1003: Syntax error, ']' expected + // case [[[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(4, 13), + // (4,13): error CS1003: Syntax error, ']' expected + // case [[[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(4, 13), + // (4,13): error CS1003: Syntax error, ']' expected + // case [[[: + Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments("]").WithLocation(4, 13) + ); + N(SyntaxKind.SwitchStatement); + { + N(SyntaxKind.SwitchKeyword); + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "obj"); + } + N(SyntaxKind.CloseParenToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SwitchSection); + { + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + M(SyntaxKind.CloseBracketToken); + } + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.CasePatternSwitchLabel); + { + N(SyntaxKind.CaseKeyword); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ListPattern); + { + N(SyntaxKind.OpenBracketToken); + M(SyntaxKind.CloseBracketToken); + } + M(SyntaxKind.CloseBracketToken); + } + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ColonToken); + } + N(SyntaxKind.BreakStatement); + { + N(SyntaxKind.BreakKeyword); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + EOF(); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens; diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs index b7a7bafc05a6f..f8f0d20bfe449 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs @@ -7,6 +7,7 @@ using System; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests @@ -248,5 +249,32 @@ public void Punctuation() Assert.True(SyntaxFacts.IsPunctuation(kind)); } } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67485")] + public void IsAttributeTargetSpecifier() + { + foreach (var kind in (SyntaxKind[])Enum.GetValues(typeof(SyntaxKind))) + { + switch (kind) + { + case SyntaxKind.AssemblyKeyword: + case SyntaxKind.ModuleKeyword: + case SyntaxKind.EventKeyword: + case SyntaxKind.FieldKeyword: + case SyntaxKind.MethodKeyword: + case SyntaxKind.ParamKeyword: + case SyntaxKind.PropertyKeyword: + case SyntaxKind.ReturnKeyword: + case SyntaxKind.TypeKeyword: + case SyntaxKind.TypeVarKeyword: + Assert.True(SyntaxFacts.IsAttributeTargetSpecifier(kind), $$"""IsAttributeTargetSpecific({{kind}}) should be true"""); + break; + + default: + Assert.False(SyntaxFacts.IsAttributeTargetSpecifier(kind), $$"""IsAttributeTargetSpecific({{kind}}) should be false"""); + break; + } + } + } } } diff --git a/src/Compilers/CSharp/csc/AnyCpu/csc.csproj b/src/Compilers/CSharp/csc/AnyCpu/csc.csproj index f159446c495ec..621378e9e9089 100644 --- a/src/Compilers/CSharp/csc/AnyCpu/csc.csproj +++ b/src/Compilers/CSharp/csc/AnyCpu/csc.csproj @@ -3,7 +3,7 @@ Exe - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildToolsetTargetFrameworks);net472 false true diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs index a014c55e8adc0..6580ae148a424 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/SegmentedArrayHelperTests.cs @@ -4,6 +4,11 @@ #nullable disable +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Collections.Internal; using Roslyn.Utilities; using Xunit; @@ -12,6 +17,88 @@ namespace Microsoft.CodeAnalysis.UnitTests.Collections { public class SegmentedArrayHelperTests { + [StructLayout(LayoutKind.Sequential, Size = 2)] + private struct Size2 { } + + [StructLayout(LayoutKind.Sequential, Size = 4)] + private struct Size4 { } + + [StructLayout(LayoutKind.Sequential, Size = 8)] + private struct Size8 { } + + [StructLayout(LayoutKind.Sequential, Size = 12)] + private struct Size12 { } + + [StructLayout(LayoutKind.Sequential, Size = 16)] + private struct Size16 { } + + [StructLayout(LayoutKind.Sequential, Size = 24)] + private struct Size24 { } + + [StructLayout(LayoutKind.Sequential, Size = 28)] + private struct Size28 { } + + [StructLayout(LayoutKind.Sequential, Size = 32)] + private struct Size32 { } + + [StructLayout(LayoutKind.Sequential, Size = 40)] + private struct Size40 { } + + public static IEnumerable ExplicitSizeTypes + { + get + { + yield return new object[] { typeof(Size2) }; + yield return new object[] { typeof(Size4) }; + yield return new object[] { typeof(Size8) }; + yield return new object[] { typeof(Size12) }; + yield return new object[] { typeof(Size16) }; + yield return new object[] { typeof(Size24) }; + yield return new object[] { typeof(Size28) }; + yield return new object[] { typeof(Size32) }; + yield return new object[] { typeof(Size40) }; + } + } + + [Theory] + [MemberData(nameof(ExplicitSizeTypes))] + public void ExplicitSizesAreCorrect(Type type) + { + Assert.Equal(int.Parse(type.Name[4..]), InvokeUnsafeSizeOf(type)); + } + + [Theory] + [MemberData(nameof(ExplicitSizeTypes))] + public void GetSegmentSize(Type type) + { + var getSegmentSizeMethod = typeof(SegmentedArrayHelper).GetMethod(nameof(SegmentedArrayHelper.GetSegmentSize), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type); + Assert.Equal(SegmentedArrayHelper.TestAccessor.CalculateSegmentSize(InvokeUnsafeSizeOf(type)), (int)getSegmentSizeMethod.Invoke(null, null)); + } + + [Theory] + [MemberData(nameof(ExplicitSizeTypes))] + public void GetSegmentShift(Type type) + { + var getSegmentShiftMethod = typeof(SegmentedArrayHelper).GetMethod(nameof(SegmentedArrayHelper.GetSegmentShift), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type); + var segmentSize = SegmentedArrayHelper.TestAccessor.CalculateSegmentSize(InvokeUnsafeSizeOf(type)); + Assert.Equal(SegmentedArrayHelper.TestAccessor.CalculateSegmentShift(segmentSize), (int)getSegmentShiftMethod.Invoke(null, null)); + } + + [Theory] + [MemberData(nameof(ExplicitSizeTypes))] + public void GetOffsetMask(Type type) + { + var getOffsetMaskMethod = typeof(SegmentedArrayHelper).GetMethod(nameof(SegmentedArrayHelper.GetOffsetMask), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type); + var segmentSize = SegmentedArrayHelper.TestAccessor.CalculateSegmentSize(InvokeUnsafeSizeOf(type)); + Assert.Equal(SegmentedArrayHelper.TestAccessor.CalculateOffsetMask(segmentSize), (int)getOffsetMaskMethod.Invoke(null, null)); + } + + private static int InvokeUnsafeSizeOf(Type type) + { + var unsafeSizeOfMethod = typeof(Unsafe).GetMethod(nameof(Unsafe.SizeOf)).MakeGenericMethod(type); + return (int)unsafeSizeOfMethod.Invoke(null, null); + } + [Theory] [InlineData(1)] [InlineData(2)] diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/TemporaryArrayTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/TemporaryArrayTests.cs index 32aa14002d0af..07f981a25f40b 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/TemporaryArrayTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/TemporaryArrayTests.cs @@ -204,5 +204,39 @@ public void TestReverseContents([CombinatorialRange(0, 6)] int initialItems) for (var i = 0; i < initialItems; i++) Assert.Equal(array[i], initialItems - 1 - i); } + + [Theory, CombinatorialData] + public void TestRemoveLast([CombinatorialRange(0, 6)] int initialItems) + { + using var array = TemporaryArray.Empty; + for (var i = 0; i < initialItems; i++) + array.Add(i); + + if (initialItems == 0) + { + Assert.Throws(() => array.RemoveLast()); + } + else + { + var count = array.Count; + var last = array.RemoveLast(); + Assert.Equal(initialItems - 1, last); + Assert.Equal(count - 1, array.Count); + } + } + + [Theory, CombinatorialData] + public void TestContains([CombinatorialRange(0, 6)] int initialItems) + { + using var array = TemporaryArray.Empty; + for (var i = 0; i < initialItems; i++) + array.Add(i); + + for (var i = 0; i < initialItems; i++) + Assert.True(array.Contains(i)); + + Assert.False(array.Contains(-1)); + Assert.False(array.Contains(initialItems)); + } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs index 8531686f0d9b0..f17755162b283 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs @@ -2,13 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Test.Utilities; using Xunit; @@ -16,6 +15,12 @@ namespace Microsoft.CodeAnalysis.UnitTests.Collections { public class TopologicalSortTests { + private TopologicalSortAddSuccessors GetAddSuccessorsFunction(int[][] successors) + => GetAddSuccessorsFunction(successors, i => i); + + private static TopologicalSortAddSuccessors GetAddSuccessorsFunction(T[][] successors, Func toInt) + => (ref TemporaryArray builder, T value) => builder.AddRange(successors[toInt(value)].ToImmutableArray()); + [Fact] public void Test01() { @@ -29,8 +34,8 @@ public void Test01() /* 5 */ new int[] { 0, 2 }, }; - Func> succF = x => successors[x]; - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 4, 5 }, i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 4, 5 }, succF, out var sorted); Assert.True(wasAcyclic); AssertTopologicallySorted(sorted, succF, "Test01"); Assert.Equal(6, sorted.Length); @@ -50,8 +55,8 @@ public void Test01b() /* 5 */ new string[] { "0", "2" }, }; - Func> succF = x => successors[int.Parse(x)]; - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { "4", "5" }, i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors, x => int.Parse(x)); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { "4", "5" }, succF, out var sorted); Assert.True(wasAcyclic); AssertTopologicallySorted(sorted, succF, "Test01"); Assert.Equal(6, sorted.Length); @@ -73,8 +78,8 @@ public void Test02() /* 7 */ new int[] { } }; - Func> succF = x => successors[x]; - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1, 6 }, i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1, 6 }, succF, out var sorted); Assert.True(wasAcyclic); AssertTopologicallySorted(sorted, succF, "Test02"); Assert.Equal(7, sorted.Length); @@ -97,7 +102,8 @@ public void TestCycle() }; // 1 -> 4 -> 3 -> 5 -> 1 - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1 }, x => successors[x].ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1 }, succF, out var sorted); Assert.False(wasAcyclic); } @@ -140,8 +146,8 @@ public void TestRandom(int seed) } // Perform a topological sort and check it. - Func> succF = x => successors[x]; - var wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), succF, out var sorted); Assert.True(wasAcyclic); Assert.Equal(numberOfNodes, sorted.Length); AssertTopologicallySorted(sorted, succF, $"TestRandom(seed: {seed})"); @@ -155,7 +161,7 @@ public void TestRandom(int seed) // time. successors[possibleSort[0]] = successors[possibleSort[0]].Concat(new int[] { possibleSort[numberOfNodes - 1] }).ToArray(); - wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), i => succF(i).ToImmutableArray(), out sorted); + wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), succF, out sorted); Assert.False(wasAcyclic); // where @@ -203,13 +209,18 @@ public void TestLots() } } - private void AssertTopologicallySorted(ImmutableArray sorted, Func> successors, string message = null) + private void AssertTopologicallySorted(ImmutableArray sorted, TopologicalSortAddSuccessors addSuccessors, string? message = null) { var seen = new HashSet(); + using var successors = TemporaryArray.Empty; for (int i = sorted.Length - 1; i >= 0; i--) { var n = sorted[i]; - foreach (var succ in successors(n)) + + successors.Clear(); + addSuccessors(ref successors.AsRef(), n); + + foreach (var succ in successors) { Assert.True(seen.Contains(succ), message); } diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj index b25c8696df1f3..76f8c63559e8e 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj +++ b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj @@ -6,7 +6,7 @@ Library Microsoft.CodeAnalysis.BuildTasks en-US - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildToolsetTargetFrameworks);net472 true + + - 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 100% rename from src/EditorFeatures/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs rename to src/Features/CSharpTest/ConvertToRecord/ConvertToRecordCodeRefactoringTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractLocalFunctionTests.cs rename to src/Features/CSharpTest/ExtractMethod/ExtractLocalFunctionTests.cs diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs b/src/Features/CSharpTest/ExtractMethod/ExtractMethodTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs rename to src/Features/CSharpTest/ExtractMethod/ExtractMethodTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/GenerateVariable/GenerateVariableTests.cs rename to src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs rename to src/Features/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs rename to src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs rename to src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs rename to src/Features/CSharpTest/MoveStaticMembers/CSharpMoveStaticMembersTests.cs 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 100% rename from src/EditorFeatures/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs rename to src/Features/CSharpTest/RemoveUnnecessaryCast/RemoveUnnecessaryCastTests_AsTests.cs diff --git a/src/EditorFeatures/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs b/src/Features/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs rename to src/Features/CSharpTest/RemoveUnusedVariable/RemoveUnusedVariableTests.cs 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 08effecb82d7c..b8b7fddb8782b 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportFeatureService.cs @@ -7,14 +7,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; @@ -97,7 +94,6 @@ private async Task> GetFixesInCurrentProcessAsy using var _ = ArrayBuilder.GetInstance(out var result); if (node != null) { - using (Logger.LogBlock(FunctionId.Refactoring_AddImport, cancellationToken)) { if (!cancellationToken.IsCancellationRequested) @@ -109,13 +105,15 @@ private async Task> GetFixesInCurrentProcessAsy document, semanticModel, diagnosticId, node, maxResults, symbolSearchService, options, packageSources, cancellationToken).ConfigureAwait(false); - // Nothing found at all. No need to proceed. foreach (var reference in allSymbolReferences) { cancellationToken.ThrowIfCancellationRequested(); var fixData = await reference.TryGetFixDataAsync(document, node, options.CleanupOptions, cancellationToken).ConfigureAwait(false); result.AddIfNotNull(fixData); + + if (result.Count > maxResults) + break; } } } @@ -142,9 +140,7 @@ private async Task> FindResultsAsync( // Look for exact matches first: var exactReferences = await FindResultsAsync(projectToAssembly, referenceToCompilation, project, maxResults, finder, exact: true, cancellationToken: cancellationToken).ConfigureAwait(false); if (exactReferences.Length > 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 @@ -168,12 +164,12 @@ private async Task> FindResultsAsync( 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 @@ -195,38 +191,35 @@ await FindResultsInAllSymbolsInStartingProjectAsync( } } - 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) { @@ -237,24 +230,23 @@ private static async Task FindResultsInUnreferencedProjectSourceSymbolsAsync( // 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 @@ -265,12 +257,11 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( 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) { @@ -281,12 +272,13 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( // 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); } /// @@ -321,38 +313,25 @@ private async Task FindResultsInUnreferencedMetadataSymbolsAsync( 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(); } } @@ -456,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 @@ public MetadataSymbolsSearchScope( 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 @@ protected override async Task> FindDeclarationsAsync( 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 @@ protected override async Task> FindDeclarationsAsync( { 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 static void CalculateContext( /// to the s 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 bool ArityAccessibilityAndAttributeContextAreCorrect( /// 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> GetReferencesForMatchingName /// 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> GetReferencesForMatchingFiel 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> GetReferencesForMatchingFiel { // 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 ImmutableArray> GetViableExtensionMethodsWor /// 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 @@ await FindNugetOrReferenceAssemblyTypeReferencesAsync( } private async Task FindNugetOrReferenceAssemblyTypeReferencesAsync( - ArrayBuilder allReferences, TSimpleNameSyntax nameNode, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, bool inAttributeContext, CancellationToken cancellationToken) { @@ -63,7 +62,7 @@ await FindNugetOrReferenceAssemblyTypeReferencesWorkerAsync( } 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 @@ await FindNugetTypeReferencesAsync( } private async Task FindReferenceAssemblyTypeReferencesAsync( - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, @@ -110,7 +109,7 @@ await HandleReferenceAssemblyReferenceAsync( private async Task FindNugetTypeReferencesAsync( string sourceName, string sourceUrl, - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, string name, int arity, @@ -132,7 +131,7 @@ private async Task FindNugetTypeReferencesAsync( } private async Task HandleReferenceAssemblyReferenceAsync( - ArrayBuilder allReferences, + ConcurrentQueue allReferences, TSimpleNameSyntax nameNode, Project project, bool isAttributeSearch, @@ -154,20 +153,20 @@ private async Task HandleReferenceAssemblyReferenceAsync( } 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/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/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/DesignerAttribute/DesignerAttributeDiscoveryService.cs b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs index 31ae00f85f298..ff979cbe1da97 100644 --- a/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs +++ b/src/Features/Core/Portable/DesignerAttribute/DesignerAttributeDiscoveryService.cs @@ -89,7 +89,7 @@ static async Task HasDesignerCategoryTypeAsync( 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 async Task ProcessProjectAsync( // 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 diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 5cce53b0c9bd7..84ec3c0066259 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -3267,10 +3267,9 @@ private async Task> AnalyzeSemanticsAsync( } // updating generic methods and types - if (InGenericContext(oldSymbol, out var oldIsGenericMethod)) + if (InGenericContext(oldSymbol) && !capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod)) { - var rudeEdit = oldIsGenericMethod ? RudeEditKind.GenericMethodTriviaUpdate : RudeEditKind.GenericTypeTriviaUpdate; - diagnostics.Add(new RudeEditDiagnostic(rudeEdit, diagnosticSpan, newEditNode, new[] { GetDisplayName(newEditNode) })); + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, diagnosticSpan, newEditNode, new[] { GetDisplayName(newEditNode) })); continue; } @@ -3707,16 +3706,13 @@ private void ReportMemberOrLambdaBodyUpdateRudeEdits( diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, GetDiagnosticSpan(newDeclaration, EditKind.Update))); } - var newIsGenericMethod = false; - var oldInGenericLocalContext = false; - var newInGenericLocalContext = false; - if (InGenericContext(oldMember, out var oldIsGenericMethod) || - InGenericContext(newMember, out newIsGenericMethod) || - (oldInGenericLocalContext = InGenericLocalContext(oldBody, memberBodyMatch.OldRoot)) || - (newInGenericLocalContext = InGenericLocalContext(newBody, memberBodyMatch.NewRoot))) + if ((InGenericContext(oldMember) || + InGenericContext(newMember) || + InGenericLocalContext(oldBody, memberBodyMatch.OldRoot) || + InGenericLocalContext(newBody, memberBodyMatch.NewRoot)) && + !capabilities.Grant(EditAndContinueCapabilities.GenericAddFieldToExistingType)) { - var rudeEdit = oldIsGenericMethod || newIsGenericMethod || oldInGenericLocalContext || newInGenericLocalContext ? RudeEditKind.GenericMethodUpdate : RudeEditKind.GenericTypeUpdate; - diagnostics.Add(new RudeEditDiagnostic(rudeEdit, GetDiagnosticSpan(newDeclaration, EditKind.Update), newDeclaration)); + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, GetDiagnosticSpan(newDeclaration, EditKind.Update), newDeclaration, new[] { GetDisplayName(newDeclaration) })); } } } @@ -4016,13 +4012,12 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( } // updating within generic context - var oldIsGenericMethod = false; - var newIsGenericMethod = false; if (rudeEdit == RudeEditKind.None && oldSymbol is not INamedTypeSymbol and not ITypeParameterSymbol and not IParameterSymbol && - (InGenericContext(oldSymbol, out oldIsGenericMethod) || InGenericContext(newSymbol, out newIsGenericMethod))) + (InGenericContext(oldSymbol) || InGenericContext(newSymbol)) && + !capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod)) { - rudeEdit = oldIsGenericMethod || newIsGenericMethod ? RudeEditKind.GenericMethodUpdate : RudeEditKind.GenericTypeUpdate; + rudeEdit = RudeEditKind.UpdatingGenericNotSupportedByRuntime; } if (rudeEdit != RudeEditKind.None) @@ -4558,7 +4553,7 @@ static bool IsSecurityAttribute(INamedTypeSymbol namedTypeSymbol) /// private bool CanRenameOrChangeSignature(ISymbol oldSymbol, ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities, CancellationToken cancellationToken) => CanAddNewMemberToExistingType(newSymbol, capabilities, cancellationToken) && - CanUpdateMemberBody(oldSymbol, newSymbol); + CanUpdateMemberBody(oldSymbol, newSymbol, capabilities); private bool CanAddNewMemberToExistingType(ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities, CancellationToken cancellationToken) { @@ -4575,19 +4570,19 @@ private bool CanAddNewMemberToExistingType(ISymbol newSymbol, EditAndContinueCap } // 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, out _)) + if (newSymbol is not INamedTypeSymbol && InGenericContext(newSymbol)) { - return false; + requiredCapabilities |= newSymbol is IFieldSymbol ? EditAndContinueCapabilities.GenericAddFieldToExistingType : EditAndContinueCapabilities.GenericAddMethodToExistingType; } return capabilities.Grant(requiredCapabilities); } - private static bool CanUpdateMemberBody(ISymbol oldSymbol, ISymbol newSymbol) + private static bool CanUpdateMemberBody(ISymbol oldSymbol, ISymbol newSymbol, EditAndContinueCapabilitiesGrantor capabilities) { - if (InGenericContext(oldSymbol, out _) || InGenericContext(newSymbol, out _)) + if (InGenericContext(oldSymbol) || InGenericContext(newSymbol)) { - return false; + return capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod); } return true; @@ -5247,9 +5242,10 @@ private void ReportLambdaAndClosureRudeEdits( oldStateMachineInfo, newStateMachineInfo); - if (IsGenericLocalFunction(oldLambda) || IsGenericLocalFunction(newLambda)) + if ((IsGenericLocalFunction(oldLambda) || IsGenericLocalFunction(newLambda)) && + !capabilities.Grant(EditAndContinueCapabilities.GenericUpdateMethod)) { - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.GenericMethodUpdate, GetDiagnosticSpan(newLambda, EditKind.Update), newLambda, new[] { GetDisplayName(newLambda) })); + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, GetDiagnosticSpan(newLambda, EditKind.Update), newLambda, new[] { GetDisplayName(newLambda) })); } } } @@ -5466,7 +5462,7 @@ select clausesByQuery.First()) var containingTypeDeclaration = TryGetContainingTypeDeclaration(newMemberBody); var isInInterfaceDeclaration = containingTypeDeclaration != null && IsInterfaceDeclaration(containingTypeDeclaration); - var isNewMemberInGenericContext = InGenericContext(newMember, out _); + var isNewMemberInGenericContext = InGenericContext(newMember); var newHasLambdaBodies = newHasLambdasOrLocalFunctions; while (newHasLambdaBodies) @@ -5526,7 +5522,7 @@ bool CanAddNewLambda(SyntaxNode newLambda, SyntaxNode newLambdaBody1, SyntaxNode if (isNewMemberInGenericContext || inGenericLocalContext) { - return false; + requiredCapabilities |= EditAndContinueCapabilities.GenericAddMethodToExistingType; } // Static lambdas are cached in static fields, unless in generic local functions. @@ -5540,6 +5536,12 @@ bool CanAddNewLambda(SyntaxNode newLambda, SyntaxNode newLambdaBody1, SyntaxNode 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) + { + requiredCapabilities |= EditAndContinueCapabilities.GenericAddFieldToExistingType; + } } // If the old verison of the method had any lambdas the nwe know a closure type exists and a new one isn't needed. @@ -6207,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; @@ -6215,20 +6217,17 @@ 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; } } 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 eb7fb59bc9dcd..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)); @@ -157,6 +152,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di 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 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/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 3a3dad089de9c..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, @@ -139,5 +139,6 @@ internal enum RudeEditKind : ushort ChangingTypeNotSupportedByRuntime = 110, DeleteNotSupportedByRuntime = 111, UpdatingStateMachineMethodNotSupportedByRuntime = 112, + UpdatingGenericNotSupportedByRuntime = 113, } } 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/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index e2918a08df2d3..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,9 @@ 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. diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 31b306a204dec..3b57a428d8359 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -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 @@ await ProcessIndexAsync( // 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 @@ private static async Task ProcessIndexAsync( 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/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/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 1c5b9a0ff15ae..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index c3eda6b98a89a..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index fe0531cf988d4..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index e51b782ed02bd..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index afd02ee9ab330..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index f72ce75eee8b2..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} を更新するには、アプリケーションを再起動する必要があります。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index acec2103159be..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}을(를) 업데이트하려면 응용 프로그램을 다시 시작해야 합니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 5eae28780fe1e..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index feae5d5bafa57..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 1ecd51c375abc..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} вокруг активного оператора требуется перезапустить приложение. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 423e99126879e..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. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index bdef208b6e923..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} 需要重新启动应用程序。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 723823d5cc68c..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} 需要重新啟動應用程式。 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/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index 24fb3276f8cc3..128cf10e4622f 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -382,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_GetDiagnosticsForSpan.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs index 4d6971b70a2c8..e81f86e1b666d 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.cs @@ -299,6 +299,13 @@ static bool ShouldIncludeAnalyzer( 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 && !owner.DiagnosticAnalyzerInfoCache.GetDiagnosticDescriptors(analyzer).Any(static (a, shouldIncludeDiagnostic) => shouldIncludeDiagnostic(a.Id), shouldIncludeDiagnostic)) 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/Handler/CodeActions/CodeActionHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 067c35c239231..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 @@ static VSInternalCodeAction[] GenerateNestedVSCodeActions( 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; } /// diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs index c7b52ccb984f7..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 TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) 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,6 +109,9 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) } 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(); // Don't apply changes in the presence of any non-document changes for now. Note though that LSP does @@ -155,6 +160,19 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) 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 @@ -203,34 +221,34 @@ await AddTextDocumentDeletionsAsync( // Added documents await AddTextDocumentAdditionsAsync( projectChanges.SelectMany(pc => pc.GetAddedDocuments()), - applyChangesOperation.ChangedSolution.GetDocument).ConfigureAwait(false); + newSolution.GetDocument).ConfigureAwait(false); // Added analyzer config documents await AddTextDocumentAdditionsAsync( projectChanges.SelectMany(pc => pc.GetAddedAnalyzerConfigDocuments()), - applyChangesOperation.ChangedSolution.GetAnalyzerConfigDocument).ConfigureAwait(false); + newSolution.GetAnalyzerConfigDocument).ConfigureAwait(false); // Added additional documents await AddTextDocumentAdditionsAsync( projectChanges.SelectMany(pc => pc.GetAddedAdditionalDocuments()), - applyChangesOperation.ChangedSolution.GetAdditionalDocument).ConfigureAwait(false); + 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); } @@ -290,28 +308,56 @@ async Task AddTextDocumentEditsAsync( 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 CodeActionsHandler( 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/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/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 TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) 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/SemanticTokens/SemanticTokensHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs index 230a46d611f2d..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,61 +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, CancellationToken cancellationToken) { + var tokenTypesToIndex = SemanticTokensSchema.GetSchema(capabilities).TokenTypeToIndex; + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -93,7 +49,7 @@ internal static async Task ComputeSemanticTokensDataAsync( // 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( @@ -214,9 +170,10 @@ static void ConvertToSingleLineSpan( } private static int[] ComputeTokens( + ClientCapabilities capabilities, TextLineCollection lines, ClassifiedSpan[] classifiedSpans, - Dictionary tokenTypesToIndex) + IReadOnlyDictionary tokenTypesToIndex) { using var _ = ArrayBuilder.GetInstance(classifiedSpans.Length, out var data); @@ -225,11 +182,13 @@ private static int[] ComputeTokens( 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); @@ -245,7 +204,8 @@ private static int ComputeNextToken( ref int lastStartCharacter, ClassifiedSpan[] classifiedSpans, int currentClassifiedSpanIndex, - Dictionary tokenTypesToIndex, + IReadOnlyDictionary tokenTypeMap, + IReadOnlyDictionary tokenTypesToIndex, out int deltaLineOut, out int startCharacterDeltaOut, out int tokenLengthOut, @@ -306,7 +266,7 @@ private static int ComputeNextToken( { // 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 @@ -326,17 +286,17 @@ private static int ComputeNextToken( 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 481335a21e58e..8cc34d655ca22 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs @@ -55,9 +55,10 @@ 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, cancellationToken).ConfigureAwait(false); 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/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/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 WorkspaceEdit GenerateWorkspaceEdit( } } }; + + 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/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/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 8d03d12bf3236..6cd2215301e52 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs @@ -27,70 +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); - 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, 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, // '}' - }, - }; + 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 + { + 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[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(expectedResults.Data), ConvertToReadableFormat(results.Data)); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); } [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_PartialDocAsync(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, 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 = @@ -99,35 +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, 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, // '}' - }, - }; + 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 + { + expectedResults.Data = new int[] + { + // 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).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_StringLiteral_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace) + public async Task TestGetSemanticTokensRange_StringLiteral_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) { var markup = @"{|caret:|}class C @@ -141,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, 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; @@ -194,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, 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; @@ -262,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: options, cancellationToken: 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) { - 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[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 + { + expectedResults.Data = new int[] + { + // Line | Char | Len | Token type | Modifier + 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/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 75fbf4e095626..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,13 +429,14 @@ private static async Task GenerateSemanticTokensAsync( { // 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, options: Classification.ClassificationOptions.Default, cancellationToken: CancellationToken.None); 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 @@ private static async Task GenerateAsync( 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 @@ private static async Task GenerateAsync( 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/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/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/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj b/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj index d8338f1ba9207..d6ed862178b3c 100644 --- a/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj +++ b/src/Features/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Features.vbproj @@ -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 100% rename from src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb rename to src/Features/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb 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 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb rename to src/Features/VisualBasicTest/InlineTemporary/InlineTemporaryTests.vb 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 100% rename from src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb rename to src/Features/VisualBasicTest/IntroduceVariable/IntroduceVariableTests.vb 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 100% rename from src/EditorFeatures/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb rename to src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb b/src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb rename to src/Features/VisualBasicTest/SimplifyTypeNames/SimplifyTypeNamesTests_FixAllTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/SpellCheck/SpellCheckSpanTests.vb b/src/Features/VisualBasicTest/SpellCheck/SpellCheckSpanTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SpellCheck/SpellCheckSpanTests.vb rename to src/Features/VisualBasicTest/SpellCheck/SpellCheckSpanTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Spellcheck/SpellcheckTests.vb b/src/Features/VisualBasicTest/SpellCheck/SpellcheckTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Diagnostics/Spellcheck/SpellcheckTests.vb rename to src/Features/VisualBasicTest/SpellCheck/SpellcheckTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithNext.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_ElseIf_WithPrevious.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithNext.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeConsecutiveIfStatementsTests_Statements_WithPrevious.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithNested.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/MergeNestedIfStatementsTests_WithOuter.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoConsecutiveIfStatementsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.vb b/src/Features/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.vb rename to src/Features/VisualBasicTest/SplitOrMergeIfStatements/SplitIntoNestedIfStatementsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/UseNamedArguments/UseNamedArgumentsTests.vb b/src/Features/VisualBasicTest/UseNamedArguments/UseNamedArgumentsTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/UseNamedArguments/UseNamedArgumentsTests.vb rename to src/Features/VisualBasicTest/UseNamedArguments/UseNamedArgumentsTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Utilities/CodeSnippets.vb b/src/Features/VisualBasicTest/Utilities/CodeSnippets.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Utilities/CodeSnippets.vb rename to src/Features/VisualBasicTest/Utilities/CodeSnippets.vb diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb b/src/Features/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb rename to src/Features/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb b/src/Features/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb rename to src/Features/VisualBasicTest/Wrapping/ArgumentWrappingTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/BinaryExpressionWrappingTests.vb b/src/Features/VisualBasicTest/Wrapping/BinaryExpressionWrappingTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Wrapping/BinaryExpressionWrappingTests.vb rename to src/Features/VisualBasicTest/Wrapping/BinaryExpressionWrappingTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/ChainedExpressionWrappingTests.vb b/src/Features/VisualBasicTest/Wrapping/ChainedExpressionWrappingTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Wrapping/ChainedExpressionWrappingTests.vb rename to src/Features/VisualBasicTest/Wrapping/ChainedExpressionWrappingTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb b/src/Features/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb rename to src/Features/VisualBasicTest/Wrapping/InitializerExpressionWrappingTests.vb diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/ParameterWrappingTests.vb b/src/Features/VisualBasicTest/Wrapping/ParameterWrappingTests.vb similarity index 100% rename from src/EditorFeatures/VisualBasicTest/Wrapping/ParameterWrappingTests.vb rename to src/Features/VisualBasicTest/Wrapping/ParameterWrappingTests.vb diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs index 67cebd97ee977..f1d15a0399e95 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.LazyRemoteService.cs @@ -34,7 +34,7 @@ private sealed class LazyRemoteService public LazyRemoteService(InteractiveHost host, InteractiveHostOptions options, int instanceId, bool skipInitialization) { - _lazyInitializedService = new AsyncLazy(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} \"{culture.Name}\"", + Arguments = $"{pipeName} {currentProcessId} \"{culture.Name}\" \"{uiCulture.Name}\"", WorkingDirectory = Host._initialWorkingDirectory, CreateNoWindow = true, UseShellExecute = false, diff --git a/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs b/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs index ddadfb273b2ec..5a30b0362c47a 100644 --- a/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs +++ b/src/Interactive/Host/Interactive/Core/InteractiveHost.Service.cs @@ -532,7 +532,7 @@ private async Task InitializeContextAsync( 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 @@ public InteractiveHostOptions( string hostPath, string? initializationFilePath, CultureInfo culture, + CultureInfo uiCulture, InteractiveHostPlatform platform) { Contract.ThrowIfNull(hostPath); @@ -44,6 +50,7 @@ public InteractiveHostOptions( HostPath = hostPath; InitializationFilePath = initializationFilePath; Culture = culture; + UICulture = uiCulture; Platform = platform; } @@ -51,6 +58,7 @@ public static InteractiveHostOptions CreateFromDirectory( string hostDirectory, string? initializationFileName, CultureInfo culture, + CultureInfo uiCulture, InteractiveHostPlatform platform) { var hostSubdirectory = (platform == InteractiveHostPlatform.Core) ? "Core" : "Desktop"; @@ -59,7 +67,7 @@ public static InteractiveHostOptions CreateFromDirectory( 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 c94695ff1a9be..6d1fe53cd9e2a 100644 --- a/src/Interactive/HostProcess/InteractiveHostEntryPoint.cs +++ b/src/Interactive/HostProcess/InteractiveHostEntryPoint.cs @@ -22,17 +22,17 @@ 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 == 3, "Expecting arguments: "); + Contract.ThrowIfFalse(args.Length == 4, "Expecting arguments: "); var pipeName = args[0]; var clientProcessId = int.Parse(args[1], CultureInfo.InvariantCulture); - var cultureName = args[2]; - var culture = new CultureInfo(cultureName); + var culture = new CultureInfo(args[2]); + var uiCulture = new CultureInfo(args[3]); CultureInfo.CurrentCulture = culture; - CultureInfo.CurrentUICulture = culture; + CultureInfo.CurrentUICulture = uiCulture; CultureInfo.DefaultThreadCurrentCulture = culture; - CultureInfo.DefaultThreadCurrentUICulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = uiCulture; Control? control = null; using (var resetEvent = new ManualResetEventSlim(false)) 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 a6d1756a0b567..85c48a4ea4942 100644 --- a/src/Interactive/csi/csi.csproj +++ b/src/Interactive/csi/csi.csproj @@ -5,7 +5,7 @@ Exe CSharpInteractive - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildToolsetTargetFrameworks);net472 false true diff --git a/src/Interactive/vbi/vbi.vbproj b/src/Interactive/vbi/vbi.vbproj index 113a6683e5ff7..723d361659ad3 100644 --- a/src/Interactive/vbi/vbi.vbproj +++ b/src/Interactive/vbi/vbi.vbproj @@ -5,7 +5,7 @@ Exe Sub Main - $(SourceBuildTargetFrameworksNetFx) + $(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 12f3cfa9bde1f..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 @@  - $(SourceBuildTargetFrameworksNetFx) + net472;$(SourceBuildToolsetTargetFramework) true Microsoft.Net.Compilers.Toolset @@ -51,10 +51,10 @@ <_File Include="@(DesktopCompilerArtifact)" TargetDir="tasks/net472"/> <_File Include="@(DesktopCompilerResourceArtifact)" TargetDir="tasks/net472"/> - <_File Include="@(CoreClrCompilerBuildArtifact)" TargetDir="tasks/$(TargetFramework)"/> - <_File Include="@(CoreClrCompilerToolsArtifact)" TargetDir="tasks/$(TargetFramework)"/> - <_File Include="@(CoreClrCompilerBinArtifact)" TargetDir="tasks/$(TargetFramework)/bincore"/> - <_File Include="@(CoreClrCompilerBinRuntimesArtifact)" TargetDir="tasks/$(TargetFramework)/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/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/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/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/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/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index 13c3f3ac833b5..4ae1a89071acd 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -426,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 873f6de8f2014..6fdfa6712f82a 100644 --- a/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Remote/VisualStudioRemoteHostClientProvider.cs @@ -37,7 +37,7 @@ internal sealed class Factory : IWorkspaceServiceFactory private readonly IThreadingContext _threadingContext; private readonly object _gate = new(); - private VisualStudioRemoteHostClientProvider? _cachedInstance; + private VisualStudioRemoteHostClientProvider? _cachedVSInstance; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -70,13 +70,19 @@ workspaceServices.Workspace is not (VisualStudioWorkspace or PreviewWorkspace) | lock (_gate) { - // Either we have no client provider, or we should be asking to get the client for a workspace that - // also shares our services. This should always be true as we only support this for the VS and - // Preview workspaces, and the latter is created with the services of the former. - Debug.Assert(_cachedInstance is null || _cachedInstance.Services.WorkspaceServices.HostServices == workspaceServices.SolutionServices.WorkspaceServices.HostServices); - - _cachedInstance ??= new VisualStudioRemoteHostClientProvider(workspaceServices.SolutionServices, _globalOptions, _vsServiceProvider, _threadingContext, _listenerProvider, _callbackDispatchers); - return _cachedInstance; + // 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; } } } diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index 226767d38ad64..3ffa9836ba44e 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -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/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs b/src/Workspaces/CSharp/Portable/Simplification/Reducers/AbstractCSharpReducer.AbstractReductionRewriter.cs index 2c6adb096893c..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(); @@ -153,19 +157,6 @@ protected SyntaxNode SimplifyNode( 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 @@ private SyntaxNode SimplifyDefaultExpression( } 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/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 1f2b37be84e72..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 - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildTargetFrameworks);net472 $(DefineConstants);WORKSPACE_MSBUILD true 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 async Task> FindAllDeclarationsWithNorma // 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_ProjectIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs index d7b6b30761415..abb7813a4db36 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs @@ -45,7 +45,7 @@ public static Task GetIndexAsync( { lazyIndex = s_projectToIndex.GetValue( project.State, p => new AsyncLazy( - c => CreateIndexAsync(project, c), cacheResult: true)); + c => CreateIndexAsync(project, c))); } return lazyIndex.GetValueAsync(cancellationToken); 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 @@ static async Task GetInfoForMetadataReferenceSlowAsync( // 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 @@ static async Task CreateMetadataSymbolTreeInfoAsync( 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 @@ public static Task GetInfoForSourceAssemblyAsync( 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/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 03cca8f9cd61f..27fa7e51c329c 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -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 static PatternMatcher CreatePatternMatcher( 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/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/ConstantTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs index cdedfa9553abf..1b7268ac8f73b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ConstantTextAndVersionSource.cs @@ -45,6 +45,12 @@ public Task GetValueAsync(LoadTextOptions options, CancellationT public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) => TryGetValue(out value); + 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 9fd34212f2623..c4d02b1090b24 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -107,8 +107,7 @@ protected static ValueSource CreateLazyFullyParsedTree( { 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 @@ private static ValueSource CreateLazyIncrementallyParsedTree( { 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)); 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 @@ static AsyncLazy GetReuseTreeSource( { 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 403fc2f57ce5c..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,7 +55,7 @@ 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; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs index 0e4b9676e69c5..ab0bc21554dde 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ITextAndVersionSource.cs @@ -20,6 +20,8 @@ internal interface ITextAndVersionSource 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. 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 c0ab70015feb1..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) @@ -69,6 +144,18 @@ 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); 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 @@ private 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 AsyncLazy CreateLazyLatestDocumentTopLevelChangeVersion( { 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 @@ private void GetLatestDependentVersions( } 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 54b0ed3a5de03..11ad671dd6bd4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/RecoverableTextAndVersion.cs @@ -15,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; @@ -57,11 +57,9 @@ public ITemporaryTextStorageInternal? Storage public bool TryGetValue(LoadTextOptions options, [MaybeNullWhen(false)] out TextAndVersion value) { if (TryGetInitialSourceOrRecoverableText(out var source, out var recoverableText)) - { return source.TryGetValue(options, out value); - } - 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; @@ -71,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; } @@ -143,7 +139,7 @@ public async ValueTask GetVersionAsync(LoadTextOptions options, Ca return recoverableText.Version; } - private sealed class RecoverableText : WeaklyCachedRecoverableValueSource, ITextVersionable + 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 @@ private 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/TextDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs index 3362244b596fd..21f236205b432 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocumentState.cs @@ -52,7 +52,7 @@ protected 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); 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/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/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 ReportDiagnostic GetEffectiveSeverity( 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/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 SegmentedArray CreateTokenOperation( 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 static void ApplySpecialOperations( 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 static void ApplySpecialOperations( private static void ApplyAnchorOperations( FormattingContext context, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, OperationApplier applier, CancellationToken cancellationToken) { @@ -364,9 +336,7 @@ private static void ApplyAnchorOperations( { 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 void ApplySpaceAndWrappingOperations( { // 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 a009bbcb30b39..f41c902d9fbc5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs @@ -268,7 +268,7 @@ public static void ClearAndFree(this ObjectPool(this ObjectPool> pool, List list) + public static void ClearAndFree(this ObjectPool> pool, List list, bool trim = true) { if (list == null) { @@ -277,7 +277,7 @@ public static void ClearAndFree(this ObjectPool> pool, List list) list.Clear(); - if (list.Capacity > Threshold) + if (trim && list.Capacity > Threshold) { list.Capacity = Threshold; } @@ -285,7 +285,7 @@ public static void ClearAndFree(this ObjectPool> pool, List list) pool.Free(list); } - public static void ClearAndFree(this ObjectPool> pool, SegmentedList list) + public static void ClearAndFree(this ObjectPool> pool, SegmentedList list, bool trim = true) { if (list == null) { @@ -294,7 +294,7 @@ public static void ClearAndFree(this ObjectPool> pool, Segme 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 98ad09b8e3c8b..9078f5f0fd50c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs @@ -134,13 +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.ClearAndFree(obj, pool.TrimOnFree); #endregion } } 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/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 @@ private CSharpSyntaxContext( bool isPreProcessorExpressionContext, bool isPreProcessorKeywordContext, bool isPrimaryFunctionExpressionContext, + bool isRightAfterUsingOrImportDirective, bool isRightOfNameSeparator, bool isRightSideOfNumericType, bool isStatementContext, @@ -132,6 +133,7 @@ private CSharpSyntaxContext( isPossibleTupleContext: isPossibleTupleContext, isPreProcessorDirectiveContext: isPreProcessorDirectiveContext, isPreProcessorExpressionContext: isPreProcessorExpressionContext, + isRightAfterUsingOrImportDirective: isRightAfterUsingOrImportDirective, isRightOfNameSeparator: isRightOfNameSeparator, isRightSideOfNumericType: isRightSideOfNumericType, isStatementContext: isStatementContext, @@ -291,6 +293,7 @@ private static CSharpSyntaxContext CreateContextWorker( 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 @@ protected SyntaxContext( bool isPossibleTupleContext, bool isPreProcessorDirectiveContext, bool isPreProcessorExpressionContext, + bool isRightAfterUsingOrImportDirective, bool isRightOfNameSeparator, bool isRightSideOfNumericType, bool isStatementContext, @@ -112,6 +114,7 @@ protected 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/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/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj index e487cde7f46d7..94dad8fda12a1 100644 --- a/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj +++ b/src/Workspaces/VisualBasic/Portable/Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj @@ -39,8 +39,10 @@ + +