diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index c5d209fb1fda5..5bd1db04eb95e 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -6,16 +6,4 @@ FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT}
# Set up machine requirements to build the repo
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
- && apt-get -y install --no-install-recommends cmake llvm-9 clang-9 \
- build-essential python curl git lldb-6.0 liblldb-6.0-dev \
- libunwind8 libunwind8-dev gettext libicu-dev liblttng-ust-dev \
- libssl-dev libnuma-dev libkrb5-dev zlib1g-dev ninja-build
-
-# Install V8 Engine
-SHELL ["/bin/bash", "-c"]
-
-RUN curl -sSL "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/linux/chromium-v8/v8-linux64-rel-8.5.183.zip" -o ./v8.zip \
- && unzip ./v8.zip -d /usr/local/v8 \
- && echo $'#!/usr/bin/env bash\n\
-"/usr/local/v8/d8" --snapshot_blob="/usr/local/v8/snapshot_blob.bin" "$@"\n' > /usr/local/bin/v8 \
- && chmod +x /usr/local/bin/v8
\ No newline at end of file
+ && apt-get -y install --no-install-recommends curl git
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8be9f768ee90d..942a2b624491e 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -40,3 +40,5 @@ src/Compilers/**/PublicAPI.Unshipped.txt @dotnet/roslyn-api-owners
src/Workspaces/**/PublicAPI.Unshipped.txt @dotnet/roslyn-api-owners
src/Features/**/PublicAPI.Unshipped.txt @dotnet/roslyn-api-owners
src/EditorFeatures/**/PublicAPI.Unshipped.txt @dotnet/roslyn-api-owners
+
+src/Tools/ExternalAccess/OmniSharp*/ @333fred @joerobich
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 30d1af265a8ec..33f395601404b 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -26,7 +26,6 @@
"omnisharp.disableMSBuildDiagnosticWarning": true,
"omnisharp.enableEditorConfigSupport": true,
"omnisharp.enableImportCompletion": true,
- "omnisharp.enableRoslynAnalyzers": true,
"omnisharp.useModernNet": true,
"omnisharp.enableAsyncCompletion": true,
// ms-vscode.powershell settings
@@ -35,4 +34,4 @@
"powershell.startAutomatically": false,
// ms-azure-devops.azure-pipelines settings
"azure-pipelines.customSchemaFile": ".vscode/dnceng-schema.json"
-}
\ No newline at end of file
+}
diff --git a/NuGet.config b/NuGet.config
index 8c879770d5df4..a26b4ca869d72 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -7,6 +7,8 @@
+
+
diff --git a/Roslyn.sln b/Roslyn.sln
index b90c9e295369f..475b330043b1c 100644
--- a/Roslyn.sln
+++ b/Roslyn.sln
@@ -220,20 +220,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Build.Tasks.CodeA
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "BasicResultProvider", "src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.shproj", "{3140FE61-0856-4367-9AA3-8081B9A80E35}"
EndProject
-Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "BasicResultProvider.NetFX20", "src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\NetFX20\BasicResultProvider.NetFX20.vbproj", "{76242A2D-2600-49DD-8C15-FEA07ECB1842}"
-EndProject
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Microsoft.CodeAnalysis.VisualBasic.ResultProvider", "src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\Portable\Microsoft.CodeAnalysis.VisualBasic.ResultProvider.vbproj", "{76242A2D-2600-49DD-8C15-FEA07ECB1843}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CSharpResultProvider", "src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.shproj", "{3140FE61-0856-4367-9AA3-8081B9A80E36}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpResultProvider.NetFX20", "src\ExpressionEvaluator\CSharp\Source\ResultProvider\NetFX20\CSharpResultProvider.NetFX20.csproj", "{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D3}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.ResultProvider", "src\ExpressionEvaluator\CSharp\Source\ResultProvider\Portable\Microsoft.CodeAnalysis.CSharp.ResultProvider.csproj", "{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D4}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ResultProvider", "src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.shproj", "{BB3CA047-5D00-48D4-B7D3-233C1265C065}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResultProvider.NetFX20", "src\ExpressionEvaluator\Core\Source\ResultProvider\NetFX20\ResultProvider.NetFX20.csproj", "{BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ResultProvider", "src\ExpressionEvaluator\Core\Source\ResultProvider\Portable\Microsoft.CodeAnalysis.ResultProvider.csproj", "{FA0E905D-EC46-466D-B7B2-3B5557F9428C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "vbc", "src\Compilers\VisualBasic\vbc\vbc.csproj", "{E58EE9D7-1239-4961-A0C1-F9EC3952C4C1}"
@@ -540,7 +534,6 @@ Global
src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5ff1e493-69cc-4d0b-83f2-039f469a04e1}*SharedItemsImports = 5
src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{60db272a-21c9-4e8d-9803-ff4e132392c8}*SharedItemsImports = 5
src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{699fea05-aea7-403d-827e-53cf4e826955}*SharedItemsImports = 13
- src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{76242a2d-2600-49dd-8c15-fea07ecb1842}*SharedItemsImports = 5
src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{76242a2d-2600-49dd-8c15-fea07ecb1843}*SharedItemsImports = 5
src\Analyzers\Core\Analyzers\Analyzers.projitems*{76e96966-4780-4040-8197-bde2879516f4}*SharedItemsImports = 13
src\Compilers\Core\CommandLine\CommandLine.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*SharedItemsImports = 5
@@ -562,8 +555,6 @@ Global
src\Compilers\Core\CommandLine\CommandLine.projitems*{ad6f474e-e6d4-4217-91f3-b7af1be31ccc}*SharedItemsImports = 13
src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 5
src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bb3ca047-5d00-48d4-b7d3-233c1265c065}*SharedItemsImports = 13
- src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bedc5a4a-809e-4017-9cfd-6c8d4e1847f0}*SharedItemsImports = 5
- src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d3}*SharedItemsImports = 5
src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 5
src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13
src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{cec0dce7-8d52-45c3-9295-fc7b16bd2451}*SharedItemsImports = 13
@@ -912,26 +903,14 @@ Global
{1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1DFEA9C5-973C-4179-9B1B-0F32288E1EF2}.Release|Any CPU.Build.0 = Release|Any CPU
- {76242A2D-2600-49DD-8C15-FEA07ECB1842}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {76242A2D-2600-49DD-8C15-FEA07ECB1842}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {76242A2D-2600-49DD-8C15-FEA07ECB1842}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {76242A2D-2600-49DD-8C15-FEA07ECB1842}.Release|Any CPU.Build.0 = Release|Any CPU
{76242A2D-2600-49DD-8C15-FEA07ECB1843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76242A2D-2600-49DD-8C15-FEA07ECB1843}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76242A2D-2600-49DD-8C15-FEA07ECB1843}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76242A2D-2600-49DD-8C15-FEA07ECB1843}.Release|Any CPU.Build.0 = Release|Any CPU
- {BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D3}.Release|Any CPU.Build.0 = Release|Any CPU
{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D4}.Release|Any CPU.Build.0 = Release|Any CPU
- {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0}.Release|Any CPU.Build.0 = Release|Any CPU
{FA0E905D-EC46-466D-B7B2-3B5557F9428C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA0E905D-EC46-466D-B7B2-3B5557F9428C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA0E905D-EC46-466D-B7B2-3B5557F9428C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -1400,13 +1379,10 @@ Global
{FCFA8808-A1B6-48CC-A1EA-0B8CA8AEDA8E} = {32A48625-F0AD-419D-828B-A50BDABA38EA}
{1DFEA9C5-973C-4179-9B1B-0F32288E1EF2} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
{3140FE61-0856-4367-9AA3-8081B9A80E35} = {151F6994-AEB3-4B12-B746-2ACFF26C7BBB}
- {76242A2D-2600-49DD-8C15-FEA07ECB1842} = {151F6994-AEB3-4B12-B746-2ACFF26C7BBB}
{76242A2D-2600-49DD-8C15-FEA07ECB1843} = {151F6994-AEB3-4B12-B746-2ACFF26C7BBB}
{3140FE61-0856-4367-9AA3-8081B9A80E36} = {913A4C08-898E-49C7-9692-0EF9DC56CF6E}
- {BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D3} = {913A4C08-898E-49C7-9692-0EF9DC56CF6E}
{BF9DAC1E-3A5E-4DC3-BB44-9A64E0D4E9D4} = {913A4C08-898E-49C7-9692-0EF9DC56CF6E}
{BB3CA047-5D00-48D4-B7D3-233C1265C065} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6}
- {BEDC5A4A-809E-4017-9CFD-6C8D4E1847F0} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6}
{FA0E905D-EC46-466D-B7B2-3B5557F9428C} = {998CAFE8-06E4-4683-A151-0F6AA4BFF6C6}
{E58EE9D7-1239-4961-A0C1-F9EC3952C4C1} = {C65C6143-BED3-46E6-869E-9F0BE6E84C37}
{6FD1CC3E-6A99-4736-9B8D-757992DDE75D} = {38940C5F-97FD-4B2A-B2CD-C4E4EF601B05}
diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml
index e43cb5206ac23..78a1a6cd59207 100644
--- a/azure-pipelines-official.yml
+++ b/azure-pipelines-official.yml
@@ -1,3 +1,15 @@
+trigger:
+ branches:
+ include:
+ - main
+ - main-vs-deps
+ - release/dev16.*-vs-deps
+ - release/dev17.*
+ exclude:
+ - release/dev17.0
+ - release/dev17.1
+pr: none
+
resources:
- repo: self
clean: true
@@ -180,6 +192,8 @@ stages:
/p:DotnetPublishUsingPipelines=true
/p:IgnoreIbcMergeErrors=true
condition: succeeded()
+
+ - template: eng\common\templates\steps\generate-sbom.yml
- task: PowerShell@2
displayName: Publish Assets
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 6e0c9a95799d7..9375997c9956b 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -146,7 +146,7 @@ jobs:
testArtifactName: Transport_Artifacts_Unix_Debug
configuration: Debug
testArguments: --testCoreClr
- queueName: 'Build.Ubuntu.1804.amd64.Open'
+ queueName: Build.Ubuntu.1804.Amd64.Open
- template: eng/pipelines/test-unix-job.yml
parameters:
@@ -209,6 +209,16 @@ jobs:
publishLocation: Container
condition: succeeded()
+- job: Correctness_TodoCheck
+ pool:
+ vmImage: ubuntu-20.04
+ timeoutInMinutes: 10
+ steps:
+ - template: eng/pipelines/checkout-unix-task.yml
+
+ - pwsh: eng/todo-check.ps1
+ displayName: Validate TODO/PROTOTYPE comments are not present
+
- job: Correctness_Rebuild
pool:
name: NetCore1ESPool-Public
diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md
index c64def30e833b..f1908ed8ac47d 100644
--- a/docs/Language Feature Status.md
+++ b/docs/Language Feature Status.md
@@ -21,14 +21,14 @@ efforts behind them.
| [Default in deconstruction](https://github.com/dotnet/roslyn/pull/25562) | [decon-default](https://github.com/dotnet/roslyn/tree/features/decon-default) | [Implemented](https://github.com/dotnet/roslyn/issues/25559) | [jcouv](https://github.com/jcouv) | [gafter](https://github.com/gafter) | [jcouv](https://github.com/jcouv) |
| [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) |
| [Required members](https://github.com/dotnet/csharplang/issues/3630) | [required-members](https://github.com/dotnet/roslyn/tree/features/required-members) | [In Progress](https://github.com/dotnet/roslyn/issues/57046) | [333fred](https://github.com/333fred) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | [333fred](https://github.com/333fred) |
-| [Top Level statement attribute specifiers](https://github.com/dotnet/csharplang/issues/5045) | [main-attributes](https://github.com/dotnet/roslyn/tree/features/main-attributes) | [In Progress](https://github.com/dotnet/roslyn/issues/57047) | [chsienki](https://github.com/chsienki) | TBD | [jaredpar](https://github.com/jaredpar) |
+| [Top Level statement attribute specifiers](https://github.com/dotnet/csharplang/issues/5045) | [main-attributes](https://github.com/dotnet/roslyn/tree/features/main-attributes) | [In Progress](https://github.com/dotnet/roslyn/issues/57047) | [chsienki](https://github.com/chsienki) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | [jaredpar](https://github.com/jaredpar) |
| [Primary Constructors](https://github.com/dotnet/csharplang/issues/2691) | [primary-constructors](https://github.com/dotnet/roslyn/tree/features/primary-constructors) | [In Progress](https://github.com/dotnet/roslyn/issues/57048) | TBD | TBD | [MadsTorgersen](https://github.com/MadsTorgersen) |
| [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) |
-| [Switch on ReadOnlySpan](https://github.com/dotnet/csharplang/issues/1881) | main | [In Progress](https://github.com/dotnet/roslyn/issues/59191) | [YairHalberstadt ](https://github.com/YairHalberstadt) | [cston](https://github.com/cston), [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv) |
+| [Pattern matching on `ReadOnlySpan`](https://github.com/dotnet/csharplang/issues/1881) | [patterns-span-char](https://github.com/dotnet/roslyn/tree/features/patterns-span-char) | [In Progress](https://github.com/dotnet/roslyn/issues/59191) | [YairHalberstadt ](https://github.com/YairHalberstadt) | [cston](https://github.com/cston), [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv) |
| [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) |
| [Utf8 String Literals](https://github.com/dotnet/csharplang/issues/184) | [Utf8StringLiterals](https://github.com/dotnet/roslyn/tree/features/Utf8StringLiterals) | [In Progress](https://github.com/dotnet/roslyn/issues/58848) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [RikkiGibson](https://github.com/RikkiGibson) | [MadsTorgersen](https://github.com/MadsTorgersen) |
-| [ref fields](https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md) | TBD | [In Progress](https://github.com/dotnet/roslyn/issues/59194) | TBD | TBD | [jaredpar](https://github.com/jaredpar) |
-| [checked operators](https://github.com/dotnet/csharplang/issues/4665) | TBD | [In Progress](https://github.com/dotnet/roslyn/issues/59196) | TBD | TBD | [AlekseyTs](https://github.com/AlekseyTs) |
+| [ref fields](https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md) | [ref-fields](https://github.com/dotnet/roslyn/tree/features/ref-fields) | [In Progress](https://github.com/dotnet/roslyn/issues/59194) | [cston](https://github.com/cston) | [RikkiGibson](https://github.com/RikkiGibson), [AlekseyTs](https://github.com/AlekseyTs) | [jaredpar](https://github.com/jaredpar) |
+| [checked operators](https://github.com/dotnet/csharplang/issues/4665) | [CheckedUserDefinedOperators](https://github.com/dotnet/roslyn/tree/features/CheckedUserDefinedOperators) | [In Progress](https://github.com/dotnet/roslyn/issues/59458) | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [chsienki](https://github.com/chsienki) | [AlekseyTs](https://github.com/AlekseyTs) |
# C# 10.0
diff --git a/docs/compilers/CSharp/CodeGen Differences.md b/docs/compilers/CSharp/CodeGen Differences.md
index ab0223c040be2..6af7c5c746539 100644
--- a/docs/compilers/CSharp/CodeGen Differences.md
+++ b/docs/compilers/CSharp/CodeGen Differences.md
@@ -19,15 +19,15 @@ When efficiency and debuggability are at conflict, Release and Debug make differ
**== Release (optimized)**
- The async codegen was for the most part redone in Roslyn.
+β’ The async codegen was for the most part redone in Roslyn.
- The capturing is smarter now when optimizations are enabled since it relies on precise data flow analysis. Basically only locals that cross awaits need to be lifted. Old compiler used more conservative approach and would often capture locals unnecessarily.
- The stack spilling is completely different. Instead of always present object array slot, we generate strongly typed reusable slots, but only if needed.
- Iterators - the genral principle is the same, but there were some minor refinements in the state machine.
+β’ Iterators - the genral principle is the same, but there were some minor refinements in the state machine.
- Local capturing is based on precise data flow analysis and may result in fewer local lifted into iterator class. Generally only locals whose values are alive across yield statements need to be captured.
- Valid states are now all positive and invalid states are negative. The common path in the iterator body switches on valid states and there are benefits from the set of those states being contiguous.
- Lambdas had some minor changes in caching strategy and a change in representation of non-lifting lambdas.
+β’ Lambdas had some minor changes in caching strategy and a change in representation of non-lifting lambdas.
- The caching strategy has changed to a more compact pattern that also results in fewer accesses to the caching field (in case JIT does not optimize multiple reads).
Instead of an equivalent of
@@ -47,28 +47,28 @@ return cacheField ?? (cacheFiled = {allocate new lambdaDelegate});
- non-lifting Lambda expressions are now implemented as an instance methods on singleton display classes. Since the entry point to the delegate is the instance "Invoke" method, it is cheaper at runtime to dispatch delegate invocations to the underlying implementing method if such method is also an instance method with exactly same formal signature as "Invoke".
- delegates for non-lifting Lambdas in generic methods can now be cached by Roslyn. That is mostly a welcome sideeffect of the change above.
- The string switch codegen in Rolsyn is completely new. Roslyn does not use dictionaries to avoid allocations and a potentially huge penalty when a string switch is execute for the first time. Roslyn uses a private function that maps strings to hash codes and a numeric switch. In some sense this is a partial inlining of the former technic that used static dictionaries.
+β’ The string switch codegen in Roslyn is completely new. Roslyn does not use dictionaries to avoid allocations and a potentially huge penalty when a string switch is execute for the first time. Roslyn uses a private function that maps strings to hash codes and a numeric switch. In some sense this is a partial inlining of the former technic that used static dictionaries.
- Array initializers are slightly more compact in most cases extra temporary for the array instance is avoided and dup is used instead. (I think VB always did this, but this is new for C#)
+β’ Array initializers are slightly more compact β in most cases extra temporary for the array instance is avoided and dup is used instead. (I think VB always did this, but this is new for C#)
- Leaves from nested try in many cases would not cascade through outer regions and just leave directly to the outmost region. (VB was doing that always, now C# does this too since this part of codegen is in shared library and it is also more compact)
+β’ Leaves from nested try in many cases would not cascade through outer regions and just leave directly to the outmost region. (VB was doing that always, now C# does this too since this part of codegen is in shared library and it is also more compact)
Branch threading in general may handle few more cases compared to the old compiler. Leave-to-leave case is probably the most noticeable.
- Numeric switches
+β’ Numeric switches β
- some minor changes in re-biasing and range checking in switches with nonzero smallest key.
- Slightly different strategy in choosing between binary search and computed switch buckets.
At high level switch codegen follows the same pattern as with old compiler, but we fixed some off-by-one errors that could result in unbalanced decision trees in some cases.
The end result is that overall IL could be different. Hopefully better.
- Some unnecessary locals could be eliminated or used on the stack when compiled with /o+.
+β’ Some unnecessary locals could be eliminated or used on the stack when compiled with /o+.
This is not new, old compiled did this as well, but implementation of this optimization has changed.
-There are few cases where old compiler was smarter since it did this optimization earlier (and caused numerous inconveniences to later stages).
+There are few cases where old compiler was βsmarterβ since it did this optimization earlier (and caused numerous inconveniences to later stages).
Generally the new approach handles more scenarios, so you may notice in some cases more dups used and fewer locals.
-**Cascading optimizations** note that it is possible for one optimization to enable another that may not be by itself new.
+**Cascading optimizations** β note that it is possible for one optimization to enable another that may not be by itself new.
**== Debug (not optimized)**
-TBW
\ No newline at end of file
+TBW
diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md
index 478fca0892c81..2c4fb0e2cd1ee 100644
--- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md
+++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md
@@ -1,6 +1,7 @@
ο»Ώ## This document lists known breaking changes in Roslyn in C# 10.0 which will be introduced with .NET 6.
-1. Beginning with C# 10.0, null suppression operator is no longer allowed in patterns.
+1. Beginning with C# 10.0, null suppression operator is no longer allowed in patterns.
+
```csharp
void M(object o)
{
@@ -8,7 +9,7 @@
}
```
-2. In C# 10, lambda expressions and method groups with inferred type are implicitly convertible to `System.MulticastDelegate`, and bases classes and interfaces of `System.MulticastDelegate` including `object`,
+2. In C# 10, lambda expressions and method groups with inferred type are implicitly convertible to `System.MulticastDelegate`, and bases classes and interfaces of `System.MulticastDelegate` including `object`,
and lambda expressions and method groups are implicitly convertible to `System.Linq.Expressions.Expression` and `System.Linq.Expressions.LambdaExpression`.
These are _function_type_conversions_.
@@ -92,7 +93,7 @@ These are _function_type_conversions_.
}
```
-3. In C#10, a lambda expression with inferred type may contribute an argument type that affects overload resolution.
+3. In C#10, a lambda expression with inferred type may contribute an argument type that affects overload resolution.
```csharp
using System;
@@ -109,18 +110,43 @@ These are _function_type_conversions_.
}
```
-4. In Visual Studio 17.1, `struct` type declarations with field initializers must include an explicitly declared constructor. Additionally, all fields must be definitely assigned in `struct` instance constructors that do not have a `: this()` initializer so any previously unassigned fields must be assigned from the added constructor or from field initializers.
+4. In Visual Studio 17.0.7, an error is reported in a `record struct` with a primary constructor if an explicit constructor has a `this()` initializer that invokes the implicit parameterless constructor. See [roslyn#58339](https://github.com/dotnet/roslyn/pull/58339).
- For instance, the following results in an error in 17.1:
+ For instance, the following results in an error:
```csharp
- struct S
+ record struct R(int X, int Y)
{
- int X = 1; // error: struct with field initializers must include an explicitly declared constructor
+ // error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this'
+ // initializer that calls the primary constructor or an explicitly declared constructor.
+ public R(int x) : this() { X = x; Y = 0; }
+ }
+ ```
+
+ The error could be resolved by invoking the primary constructor (as below) from the `this()` initializer, or by declaring a parameterless constructor that invokes the primary constructor.
+ ```csharp
+ record struct R(int X, int Y)
+ {
+ public R(int x) : this(x, 0) { } // ok
+ }
+ ```
+
+5. In Visual Studio 17.0.7, if a `struct` type declaration with no constructors includes initializers for some but not all fields, the compiler will report an error that all fields must be assigned.
+
+ Earlier builds of 17.0 skipped _definite assignment analysis_ for the parameterless constructor synthesized by the compiler in this scenario and did not report unassigned fields, potentially resulting in instances with uninitialized fields. The updated analysis and error reporting is consistent with explicitly declared constructors. See [roslyn#57925](https://github.com/dotnet/roslyn/pull/57925).
+
+ For instance, the following results in an error:
+ ```csharp
+ struct S // error CS0171: Field 'S.Y' must be fully assigned before control is returned to the caller
+ {
+ int X = 1;
int Y;
}
```
- The error could be resolved by adding a constructor and assigning the other field.
+ To resolve the errors, declare a parameterless constructor and assign the fields that do not have initializers, or remove the existing field initializers so the compiler does not synthesize a parameterless constructor.
+ (For compatibility with Visual Studio 17.1 which requires a `struct` with field initializers to [include an explicitly declared constructor](https://github.com/dotnet/roslyn/blob/main/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20DotNet%207.md#6), avoid adding initializers to the remaining fields without also declaring a constructor.)
+
+ For instance, the error in the example above can be resolved by adding a constructor and assigning `Y`:
```csharp
struct S
{
@@ -129,3 +155,4 @@ These are _function_type_conversions_.
public S() { Y = 0; } // ok
}
```
+
diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md
index acfba8bddc569..15ed9a97a5f4f 100644
--- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md
+++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md
@@ -1,6 +1,6 @@
## This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7.
-1. In Visual Studio 17.1, the contextual keyword `var` cannot be used as an explicit lambda return type.
+1. In Visual Studio 17.1, the contextual keyword `var` cannot be used as an explicit lambda return type.
```csharp
using System;
@@ -13,7 +13,7 @@
class var { }
```
-2. In Visual Studio 17.1, indexers that take an interpolated string handler and require the receiver as an input for the constructor cannot be used in an object initializer.
+2. In Visual Studio 17.1, indexers that take an interpolated string handler and require the receiver as an input for the constructor cannot be used in an object initializer.
```cs
using System.Runtime.CompilerServices;
@@ -35,7 +35,7 @@
}
```
-3. In Visual Studio 17.1, `ref`/`ref readonly`/`in`/`out` are not allowed to be used on return/parameters of a method attributed with `UnmanagedCallersOnly`.
+3. In Visual Studio 17.1, `ref`/`ref readonly`/`in`/`out` are not allowed to be used on return/parameters of a method attributed with `UnmanagedCallersOnly`.
https://github.com/dotnet/roslyn/issues/57025
```cs
@@ -56,7 +56,7 @@ https://github.com/dotnet/roslyn/issues/57025
static void M5(out int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.
```
-4. Beginning with C# 11.0, `Length` and `Count` properties on countable and indexable types
+4. Beginning with C# 11.0, `Length` and `Count` properties on countable and indexable types
are assumed to be non-negative for purpose of subsumption and exhaustiveness analysis of patterns and switches.
Those types can be used with implicit Index indexer and list patterns.
@@ -67,7 +67,7 @@ Those types can be used with implicit Index indexer and list patterns.
}
```
-5. Starting with Visual Studio 17.1, format specifiers in interpolated strings can not contain curly braces (either `{` or `}`). In previous versions `{{` was interpreted as an escaped `{` and `}}` was interpreted as an escaped `}` char in the format specifier. Now the first `}` char in a format specifier ends the interpolation, and any `{` char is an error.
+5. Starting with Visual Studio 17.1, format specifiers in interpolated strings can not contain curly braces (either `{` or `}`). In previous versions `{{` was interpreted as an escaped `{` and `}}` was interpreted as an escaped `}` char in the format specifier. Now the first `}` char in a format specifier ends the interpolation, and any `{` char is an error.
https://github.com/dotnet/roslyn/issues/57750
```csharp
@@ -77,3 +77,32 @@ https://github.com/dotnet/roslyn/issues/57750
//prints now: "{C}" - not "{X}}"
```
+
+6. In Visual Studio 17.1, `struct` type declarations with field initializers must include an explicitly declared constructor. Additionally, all fields must be definitely assigned in `struct` instance constructors that do not have a `: this()` initializer so any previously unassigned fields must be assigned from the added constructor or from field initializers. See [csharplang#5552](https://github.com/dotnet/csharplang/issues/5552), [roslyn#58581](https://github.com/dotnet/roslyn/pull/58581).
+
+ For instance, the following results in an error in 17.1:
+ ```csharp
+ struct S
+ {
+ int X = 1; // error CS8983: A 'struct' with field initializers must include an explicitly declared constructor.
+ int Y;
+ }
+ ```
+
+ The error could be resolved by adding a constructor and assigning the other field.
+ ```csharp
+ struct S
+ {
+ int X = 1;
+ int Y;
+ public S() { Y = 0; } // ok
+ }
+ ```
+
+7. Before Visual Studio 17.2, the C# compiler would accept incorrect default argument values involving a reference conversion of a string constant, and would emit `null` as the constant value instead of the default value specified in source. In Visual Studio 17.2, this becomes an error. See [roslyn#59806](https://github.com/dotnet/roslyn/pull/59806).
+
+ For instance, the following results in an error in 17.2:
+ ```csharp
+ void M(IEnumerable s = "hello")
+ ```
+
diff --git a/docs/contributing/Building, Debugging, and Testing on Windows.md b/docs/contributing/Building, Debugging, and Testing on Windows.md
index aa8bd58444844..f3f6be666bf54 100644
--- a/docs/contributing/Building, Debugging, and Testing on Windows.md
+++ b/docs/contributing/Building, Debugging, and Testing on Windows.md
@@ -15,9 +15,8 @@ The minimal required version of .NET Framework is 4.7.2.
## Developing with Visual Studio 2022
-1. [Visual Studio 2022 17.0 Preview](https://visualstudio.microsoft.com/vs/preview/vs2022/)
+1. Use latest [Visual Studio 2022 Preview](https://visualstudio.microsoft.com/vs/preview/vs2022/)
- Ensure C#, VB, MSBuild, .NET Core and Visual Studio Extensibility are included in the selected work loads
- - Ensure Visual Studio is on Version "17.0" or greater
- Ensure "Use previews of the .NET Core SDK" is checked in Tools -> Options -> Environment -> Preview Features
- Restart Visual Studio
1. [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/6.0) [Windows x64 installer](https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-6.0.100-windows-x64-installer)
diff --git a/docs/contributing/Compiler Test Plan.md b/docs/contributing/Compiler Test Plan.md
index 2abe570bdef9c..fd9cb54a66c2c 100644
--- a/docs/contributing/Compiler Test Plan.md
+++ b/docs/contributing/Compiler Test Plan.md
@@ -2,7 +2,8 @@ This document provides guidance for thinking about language interactions and tes
# General concerns:
- Completeness of the specification as a guide for testing (is the spec complete enough to suggest what the compiler should do in each scenario?)
-- Other external documentation
+- *Ping* for new breaking changes and general ping for partner teams (Bill, Kathleen, Mads, IDE, Razor)
+- Help review external documentation
- Backward and forward compatibility (interoperation with previous and future compilers, each in both directions)
- Error handling/recovery (missing libraries, including missing types in mscorlib; errors in parsing, ambiguous lookup, inaccessible lookup, wrong kind of thing found, instance vs static thing found, wrong type for the context, value vs variable)
- BCL (including mono) and other customer impact
@@ -133,8 +134,8 @@ Interaction with IDE, Debugger, and EnC should be worked out with relevant teams
- Compiling expressions in Immediate/Watch windows or hovering over an expression
- Compiling expressions in [DebuggerDisplay("...")]
- Assigning values in Locals/Autos/Watch windows
-
-- Edit-and-continue
+
+- [Edit-and-continue](https://github.com/dotnet/roslyn/blob/main/docs/contributing/Testing%20for%20Interactive%20readiness.md)
- Live Unit Testing (instrumentation)
diff --git a/docs/contributing/Documentation for IDE CodeStyle analyzers.md b/docs/contributing/Documentation for IDE CodeStyle analyzers.md
index a1c727b1a0ef6..657235ec39ee7 100644
--- a/docs/contributing/Documentation for IDE CodeStyle analyzers.md
+++ b/docs/contributing/Documentation for IDE CodeStyle analyzers.md
@@ -7,7 +7,7 @@
2. Each IDE diagnostic ID has a dedicated documentation page. For example:
1. Diagnostic with code style option:
2. Diagnostic without code style option:
- 3. Multiple diagnostic IDs sharing the same option(s):
+ 3. Multiple diagnostic IDs sharing the same option(s): . In this case, a [redirection](https://github.com/dotnet/docs/blob/7ae7272d643aa1c6db96cad8d09d4c2332855960/.openpublishing.redirection.fundamentals.json#L11-L14) for all diagnostic IDs must be added.
3. We have tabular indices for IDE diagnostic IDs and code style options for easier reference and navigation:
1. Primary index (rule ID + code style options):
@@ -20,9 +20,9 @@
Roslyn IDE team (@dotnet/roslyn-ide) is responsible for ensuring that the documentation for IDE analyzers is up-to-date. Whenever we add a new IDE analyzer, add new code style option(s) OR update semantics of existing IDE analyzers the following needs to be done:
-1. **Action required in [dotnet\docs](https://github.com/dotnet/docs) repo**:
+1. **Action required in [dotnet/docs](https://github.com/dotnet/docs) repo**:
1. If you _creating a Roslyn PR for an IDE analyzer_:
- 1. Please follow the steps at [Contribute docs for 'IDExxxx' rules](https://docs.microsoft.com/contribute/dotnet/dotnet-contribute-code-analysis#contribute-docs-for-idexxxx-rules) to add documentation for IDE analyzers to [dotnet\docs](https://github.com/dotnet/docs) repo.
+ 1. Please follow the steps at [Contribute docs for 'IDExxxx' rules](https://docs.microsoft.com/contribute/dotnet/dotnet-contribute-code-analysis#contribute-docs-for-idexxxx-rules) to add documentation for IDE analyzers to [dotnet/docs](https://github.com/dotnet/docs) repo.
2. Ideally, the documentation PR should be created in parallel to the Roslyn PR or immediately following the approval/merge of Roslyn PR.
2. If you are _reviewing a Roslyn PR for an IDE analyzer_:
1. Please ensure that the analyzer author is made aware of the above doc contribution requirements, especially if it is a community PR.
@@ -30,4 +30,4 @@ Roslyn IDE team (@dotnet/roslyn-ide) is responsible for ensuring that the docume
2. **No action required in Roslyn repo**: Help links have been hooked up to IDE code style analyzers in Roslyn repo in the base analyzer types, so every new IDE analyzer diagnostic ID will automatically have `https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ideXXXX` as its help link. There is no action required on the Roslyn PR to ensure this linking.
-If you feel any of the existing IDE rule docs have insufficient or incorrect information, please submit a PR to [dotnet\docs](https://github.com/dotnet/docs) repo to update the documentation.
+If you feel any of the existing IDE rule docs have insufficient or incorrect information, please submit a PR to [dotnet/docs](https://github.com/dotnet/docs) repo to update the documentation.
diff --git a/docs/contributing/Testing for Interactive readiness.md b/docs/contributing/Testing for Interactive readiness.md
new file mode 100644
index 0000000000000..dd90c7b982692
--- /dev/null
+++ b/docs/contributing/Testing for Interactive readiness.md
@@ -0,0 +1,46 @@
+
+# Compiler debugging/EnC checklist
+
+1. Sequence points are emitted correctly and corresponding syntax nodes are recognized as breakpoint spans by the IDE
+ - Verify manually by launching VS with the new compiler bits and step through and place breakpoints on all syntax nodes that should allow breakpoint
+ - Add regression compiler tests that check emitted IL with sequence points (`VerifyIL` with `sequencePoints`)
+ - Add IDE test via `src\EditorFeatures\CSharpTest\EditAndContinue\BreakpointSpansTests.cs` (implementation `src\Features\CSharp\Portable\EditAndContinue\BreakpointSpans.cs`, which deals with mapping syntax to sequence points)
+2. Sequence points in relationship with closure allocations and other hidden code (for any syntax that produces sequence points)
+ - The debugger supports manually moving the current IP (instruction pointer) using βSet Next Statement Ctlr+Shift+F10β command.
+ - The statement can be set to any sequence point in the current method.
+ - Need to make sure that sequence points are emitted so that the right hidden code gets executed.
+ - For example, the sequence point on an opening brace of a block that allocates a closure needs to precede the closure allocation instructions, so that when the IP is set to this sequence point the closure is properly allocated.
+```
+{ // closure allocated here
+ var x = 1;
+ F(() => x);
+}
+```
+3. Conditional branching has to use stloc/ldloc pattern in DEBUG build
+ - Check the instructions and sequence points emitted for e.g. an if statement in debug build. Instead of straightforward brtrue/brfalse we allocate a temp local, store the result of the condition evaluation to that local, emit hidden sequence point, load the result from the local and then branch. This supports EnC function remapping. Only need to think about this when implementing a feature that emits conditional branches with conditions that may contain arbitrary expressions. I believe there are helpers in the lowering phase that emit conditions in general, so it should be done automatically as long as the right helpers are used. But something to be aware of.
+4. Closure and lambda scopes (PDB info)
+ - If new syntax is introduced that represents some kind of lambda (anonymous methods, local functions, LINQ queries, etc.) update helpers in `src\Compilers\CSharp\Portable\Syntax\LambdaUtilities.cs` accordingly
+ - If a new scope is introduce that can declare variables that can be lifted into a closure
+ - The bound node that represents the scope needs to be associated with syntax node recognized by helpers in `src\Compilers\CSharp\Portable\Syntax\LambdaUtilities.cs` (specifically `IsClosureScope`).
+ - This requirement is enforced by an assertion in `SynthesizedClosureEnvironment` constructor.
+ - Lambda and closure syntax offsets must be emitted to the PDB (encLambdaMap custom debug information)
+ - The offset attribute of closure identifies the syntax node that's associated with the closure. This offset must be unique.
+5. When a new symbol is introduced symbol matcher might need to be updated
+ - Symbol matcher maps a symbol from one compilation to another.
+ - Synthesized symbols like closures, state machines, anonymous types, lambdas, etc. also has to be mapped.
+ - Impl: `src\Compilers\CSharp\Portable\Emitter\EditAndContinue\CSharpSymbolMatcher.cs`
+ - Tests: `src\Compilers\CSharp\Test\Emit\Emit\EditAndContinue\SymbolMatcherTests.cs`
+6. When a new syntax is introduced that may declare a user local variable or emits long lived synthesized variables (ie. state that survives between breakpoints, not just a temp within expression)
+ - Validate that the variable slots can be mapped from new to previous compilation
+ - This is implemented by `EncVariableSlotAllocator` using syntax offsets stored in PDB (`encLocalSlotMap` and `encLambdaMap` custom debug info).
+ - The current mechanism might not be sufficient to support the mapping, in which case raise the issue with the IDE team to design additional PDB info to support the mapping.
+7. Each new language feature should be covered by a test in Emit tests under Emit/EditAndContinue.
+ - Some features might just need a single test others multiple tests depending on impact on EnC.
+ - PDB tests validate these scopes. (look for `` in PDB XML). `LocalsTests.cs` EE tests also validate the scoping.
+8. When a new syntax is introduced that may declare a scope for local variables the corresponding IL scopes need to be emitted correctly in the PDB
+ - These are used by the EE to determine which variables are in scope.
+9. Test debugging experience of the feature
+ - Is useful info displayed in Watch window?
+ - Can I evaluate expressions using this feature in Watch window?
+ - Some features might require adding more custom PDB information to make the debug experience good (e.g. async, iterators, dynamic, etc).
+ - Design experience improvement and custom PDB info with IDE team.
diff --git a/docs/features/incremental-generators.md b/docs/features/incremental-generators.md
index c5ea5513c0b34..5009d395ef7a5 100644
--- a/docs/features/incremental-generators.md
+++ b/docs/features/incremental-generators.md
@@ -2,7 +2,7 @@
## Summary
-Incremental generators are intended to be a new API that exists alongside
+Incremental generators are a new API that exists alongside
[source generators](generators.md) to allow users to specify generation
strategies that can be applied in a high performance way by the hosting layer.
@@ -14,6 +14,40 @@ strategies that can be applied in a high performance way by the hosting layer.
- Support generating more items that just source texts
- Exist alongside `ISourceGenerator` based implementations
+## Simple Example
+
+We begin by defining a simple incremental generator that extracts the contents
+of additional text files and makes their contents available as compile time
+`const`s. In the following section we'll go into more depth around the concepts
+shown.
+
+```csharp
+[Generator]
+public class Generator : IIncrementalGenerator
+{
+ public void Initialize(IncrementalGeneratorInitializationContext initContext)
+ {
+ // define the execution pipeline here via a series of transformations:
+
+ // find all additional files that end with .txt
+ IncrementalValuesProvider textFiles = context.AdditionalTextsProvider.Where(static file => file.Path.EndsWith(".txt"));
+
+ // read their contents and save their name
+ IncrementalValuesProvider<(string name, string content)> namesAndContents = textFiles.Select((text, cancellationToken) => (name: Path.GetFileNameWithoutExtension(text.Path), content: text.GetText(cancellationToken)!.ToString()));
+
+ // generate a class that contains their values as const strings
+ context.RegisterSourceOutput(namesAndContents, (spc, nameAndContent) =>
+ {
+ spc.AddSource($"ConstStrings.{nameAndContent.name}", $@"
+ public static partial class ConstStrings
+ {{
+ public const string {nameAndContent.name} = ""{nameAndContent.content}"";
+ }}");
+ });
+ }
+}
+```
+
## Implementation
An incremental generator is an implementation of `Microsoft.CodeAnalysis.IIncrementalGenerator`.
@@ -42,234 +76,801 @@ public class MyGenerator : IIncrementalGenerator { ... }
An assembly can contain a mix of diagnostic analyzers, source generators and
incremental generators.
-### Initialization
-
-`IIncrementalGenerator` has an `Initialize` method that is called by the host
-(either the IDE or the command-line compiler) exactly once, regardless of the
-number of further compilations that may occur. `Initialize` passes an instance
-of `IncrementalGeneratorInitializationContext` which can be used by the
-generator to register a set of callbacks that affect how future generation
-passes will occur.
-
-Currently `IncrementalGeneratorInitializationContext` supports two callbacks:
+### Pipeline based execution
-- `RegisterForPostInitialization(GeneratorPostInitializationContext)`: the same
- callback in shape and function as supported by Source Generators today
-- `RegisterExecutionPipeline(IncrementalGeneratorPipelineContext)`: replaces Execute, described below
+`IIncrementalGenerator` has an `Initialize` method that is called by the
+host[^1] exactly once, regardless of the number of further compilations that may
+occur. For instance a host with multiple loaded projects may share the same
+generator instance across multiple projects, and will only call `Initialize` a
+single time for the lifetime of the host.
-### Pipeline based execution
+[^1]: Such as the IDE or the command-line compiler
Rather than a dedicated `Execute` method, an Incremental Generator instead
-creates an execution 'pipeline' as part of the initialization process via the
-`RegisterExecutionPipeline` method:
+defines an immutable execution pipeline as part of initialization. The
+`Initialize` method receives an instance of
+`IncrementalGeneratorInitializationContext`which is used by the generator to
+define a set of transformations.
+
```csharp
public void Initialize(IncrementalGeneratorInitializationContext initContext)
{
- initContext.RegisterExecutionPipeline(context =>
- {
- // build the pipeline...
- });
+ // define the execution pipeline here via a series of transformations:
}
```
-This pipeline is not directly executed, but instead consists of a set of steps
-that are executed on demand as the input data to the pipeline changes. Between
-each step the data produced is cached, allowing previously calculated values to
-be reused in later computations when applicable, reducing the overall
-computation required between compilations.
+The defined transformations are not executed directly at initialization, and
+instead are deferred until the data they are using changes. Conceptually this is
+similar to LINQ, where a lambda expression might not be executed until the
+enumerable is actually iterated over:
+
+**IEnumerable**:
+
+```csharp
+ var squares = Enumerable.Range(1, 10).Select(i => i * 2);
+
+ // the code inside select is not executed until we iterate the collection
+ foreach (var square in squares) { ... }
+```
+
+These transformations are used to form a directed graph of actions that can be
+executed on demand later, as the input data changes.
+
+**Incremental Generators**:
+
+```csharp
+ IncrementalValuesProvider textFiles = context.AdditionalTextsProvider.Where(static file => file.Path.EndsWith(".txt"));
+ // the code in the Where(...) above will not be executed until the value of the additional texts actually changes
+```
+
+Between each transformation, the data produced is cached, allowing previously calculated
+values to be re-used where applicable. This caching reduces the computation
+required for subsequent compilations. See [caching](#caching) for more details.
-### IncrementalValueSource<T>
+### IncrementalValue\[s\]Provider<T>
-Input data is available to the pipeline in the form of an opaque data source,
-modelled as an `IncrementalValueSource` where _T_ is the type of data that
-can be accessed in the pipeline.
+Input data is available to the pipeline in the form of opaque data sources,
+either an `IncrementalValueProvider` or `IncrementalValuesProvider` (note
+the plural _values_) where _T_ is the type of input data that is provided.
-These sources are defined up front by the compiler, and can be accessed from the
-`Values` property of the `context` passed as part of the
-`RegisterExecutionPipeline` callback. Example values sources include
+An initial set of providers are created by the host, and can be accessed from the
+`IncrementalGeneratorInitializationContext` provided during initialization.
-- Compilation
-- AdditionalTexts
-- AnalyzerConfigOptions
-- MetadataReferences
-- ParseOptions
+The currently available providers are:
-Value sources have 'zero-or-more' potential values that can be produced. For
-example, the `Compilation` will always produce a single value, whereas the
-`AdditionalTexts` will produce a variable number of values, depending on how
-many additional texts where passed to the compiler.
+- CompilationProvider
+- AdditionalTextsProvider
+- AnalyzerConfigOptionsProvider
+- MetadataReferencesProvider
+- ParseOptionsProvider
-An execution pipeline cannot access these values directly. Instead it supplies a
-set of transforms that will be applied to the data as it changes. Transforms are
-applied through a set of extension methods:
+*Note*: there is no provider for accessing syntax nodes. This is handled
+in a slightly different way. See [SyntaxValueProvider](#syntaxvalueprovider) for details.
+
+A value provider can be thought of as a 'box' that holds the value itself. An
+execution pipeline does not access the values in a value provider directly.
+
+```ascii
+IValueProvider
+ βββββββββββββββ
+ | |
+ β TSource β
+ | |
+ βββββββββββββββ
+```
+
+Instead, the generator supplies a set of transformations that ar to be applied to the
+data contained within the provider, which in turn creates a new value provider.
+
+### Select
+
+The simplest transformation is `Select`. This maps the value in one provider
+into a new provider by applying a transform to it.
+
+```ascii
+ IValueProvider IValueProvider
+ βββββββββββββββ βββββββββββββββ
+ β β Select β β
+ β TSource βββββββββββββββββββββββββββββΊβ TResult β
+ β β β β
+ βββββββββββββββ βββββββββββββββ
+```
+
+Generator transformations can be thought of as being conceptually somewhat similar to
+LINQ, with the value provider taking the place of `IEnumerable`.
+Transforms are created through a set of extension methods:
```csharp
public static partial class IncrementalValueSourceExtensions
{
// 1 => 1 transform
- public static IncrementalValueSource Transform(this IncrementalValueSource source, Func func) => ...
+ public static IncrementalValueProvider Select(this IncrementalValueProvider source, Func selector);
+ public static IncrementalValuesProvider Select(this IncrementalValuesProvider source, Func selector);
}
```
-These extension methods allow the user to perform a series of transforms,
-conceptually somewhat similar to LINQ, over the values coming from the data
-source:
+Note how the return type of these methods are also an instance of
+`IncrementalValue[s]Provider`. This allows the generator to chain multiple
+transformations together:
+
+```ascii
+ IValueProvider IValueProvider IValueProvider
+ βββββββββββββββ βββββββββββββββ βββββββββββββββ
+ β β Select β β Select β β
+ β TSource βββββββββββββββββββββββββββββΊβ TResult1 ββββββββββββββββββββββββββββΊβ TResult2 β
+ β β β β β β
+ βββββββββββββββ βββββββββββββββ βββββββββββββββ
+```
+
+Consider the following simple example:
```csharp
-initContext.RegisterExecutionPipeline(context =>
+initContext.RegisterExecutionPipeline(static context =>
{
- // get the additional text source
- IncrementalValueSource additionalTexts = context.Sources.AdditionalTexts;
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
// apply a 1-to-1 transform on each text, which represents extracting the path
- IncrementalValueSource transformed = additionalTexts.Transform(static text => text.Path);
+ IncrementalValuesProvider transformed = additionalTexts.Select(static (text, _) => text.Path);
+
+ // transform each extracted path into something else
+ IncrementalValuesProvider prefixTransform = transformed.Select(static (path, _) => "prefix_" + path);
});
```
-Note that `transformed` is similarly opaque. It represents the outcome of the
-transformation being applied to the data, but cannot be accessed directly.
+Note how `transformed` and `prefixTransform` are themselves an
+`IncrementalValuesProvider`. They represent the outcome of the transformation
+that will be applied, rather than the resulting data.
+
+### Multi Valued providers
+
+An `IncrementalValueProvider` will always provide a single value, whereas an
+`IncrementalValuesProvider` may provide zero or more values. For example the
+`CompilationProvider` will always produce a single compilation instance, whereas
+the `AdditionalTextsProvider` will produce a variable number of values,
+depending on how many additional texts where passed to the compiler.
+
+Conceptually it is simple to think about the transformation of a single item
+from an `IncrementalValueProvider`: the single item has the selector function
+applied to it which produces a single value of `TResult`.
+
+For an `IncrementalValuesProvider` however, this transformation is more
+subtle. The selector function is applied multiple times, one to each item in the
+values provider. The results of each transformation are then used to create the
+values for the resulting values provider:
+
+```ascii
+ Select
+ .......................................
+ . βββββββββββββ .
+ . selector(Item1) β β .
+ . ββββββββββββββββββΊβ Result1 βββββ .
+ . β β β β .
+IncrementalValuesProvider . β βββββββββββββ β . IncrementalValuesProvider
+ βββββββββββββ . β βββββββββββββ β . ββββββββββββββ
+ β β . β selector(Item2) β β β . β β
+ β TSource ββββββββββββββββΌβββββββββββββββββΊβ Result2 βββββΌββββββββββΊβ TResult β
+ β β . β β β β . β β
+ βββββββββββββ . β βββββββββββββ β . ββββββββββββββ
+ 3 items . β βββββββββββββ β . 3 items
+ [Item1, Item2, Item3] . β selector(Item3) β β β . [Result1, Result2, Result3]
+ . ββββββββββββββββββΊβ Result3 βββββ .
+ . β β .
+ . βββββββββββββ .
+ .......................................
+```
-The transformed steps can be further transformed, and it is also valid to
-perform multiple transformations on the same input node, essentially 'splitting'
-the pipeline into multiple streams of processing.
+It is this item-wise transformation that allows the caching to be particularly
+powerful in this model. Consider when the values inside
+`IncrementalValueProvider` change. Its likely that any given change
+will only change one item at a time rather than the whole collection (for example
+a user typing in an additional text only changes the given text, leaving the
+other additional texts unmodified).
-```csharp
- // apply a 1-to-1 transform on each text, extracting the path
- IncrementalValueSource transformed = additionalTexts.Transform(static text => text.Path);
+When this occurs the generator driver can compare the input items with the ones
+that were used previously. If they are considered to be equal then the
+transformations for those items can be skipped and the previously computed
+versions used instead. See [Comparing Items](#comparing-items) for more details.
- // split the processing into two streams of derived data
- IncrementalValueSource prefixTransform = transformed.Transform(static path => "prefix_" + path);
- IncrementalValueSource postfixTransform = transformed.Transform(static path => path + "_postfixed");
-```
+In the above diagram if `Item2` were to change we would execute the selector on
+the modified value producing a new value for `Result2`. As `Item1`and `Item3`
+are unchanged the driver is free to skip executing the selector and just use the
+cached values of `Result1` and `Result3` from the previous execution.
-### Batching
+### Select Many
-In addition to the 1-to-1 transform shown above, there are also extension
-methods for producing and consuming batches of data. For instance a given
-transform may want to produce more than one value for each input, or want to
-view all the data in a single collected view in order to make cross data
-decisions.
+In addition to the 1-to-1 transform shown above, there are also transformations
+that produce batches of data. For instance a given transformation may want to
+produce multiple values for each input. There are a set of `SelectMany` methods that allow a transformation of 1 to
+many, or many to many items:
+
+**1 to many:**
``` csharp
public static partial class IncrementalValueSourceExtensions
{
- // 1 => many (or none)
- public static IncrementalValueSource TransformMany(this IncrementalValueSource source, Func> func) => ...
+ public static IncrementalValuesProvider SelectMany(this IncrementalValueProvider source, Func> selector);
+}
+```
+
+```ascii
+ SelectMany
+ .......................................
+ . βββββββββββββ .
+ . β β .
+ . ββββΊβ Result1 βββββ .
+ . β β β β .
+ IncrementalValueProvider . β βββββββββββββ β . IncrementalValuesProvider
+ βββββββββββββ . β βββββββββββββ β . ββββββββββββββ
+ β β . selector(Item)β β β β . β β
+ β TSource ββββββββββββββββββββββββββββββΌβββΊβ Result2 βββββΌββββββββββΊβ TResult β
+ β β . β β β β . β β
+ βββββββββββββ . β βββββββββββββ β . ββββββββββββββ
+ Item . β βββββββββββββ β . 3 items
+ . β β β β . [Result1, Result2, Result3]
+ . ββββΊβ Result3 βββββ .
+ . β β .
+ . βββββββββββββ .
+ .......................................
+```
- // many => 1
- public static IncrementalValueSource BatchTransform(this IncrementalValueSource source, Func, U> func) => ...
+**Many to many:**
- // many => many (or none)
- public static IncrementalValueSource BatchTransformMany(this IncrementalValueSource source, Func, IEnumerable> func) => ...
+``` csharp
+public static partial class IncrementalValueSourceExtensions
+{
+ public static IncrementalValuesProvider SelectMany(this IncrementalValuesProvider source, Func> selector);
}
```
-In our above example we could use `BatchTransform` to collect the individual
-file paths collected, and convert them into a single collection:
+```ascii
+ SelectMany
+ ...............................................
+ . βββββββββββ .
+ . β β .
+ . ββββββΊβ Result1 βββββββββ .
+ . β β β β .
+ . β βββββββββββ β .
+ . selector(Item1) β β .
+ .βββββββββββββββββββ βββββββββββ β .
+ .β β β β .
+ IncrementalValuesProvider.β ββββββΊβ Result2 βββββββββ€ . IncrementalValuesProvider
+ βββββββββββββ .β β β β β . ββββββββββββββ
+ β β .β selector(Item2) β βββββββββββ β . β β
+ β TSource βββββββββββββββΌββββββββββββββββββ€ βββββββββββ ββββββββββββββββΊβ TResult β
+ β β .β β β β β . β β
+ βββββββββββββ .β ββββββΊβ Result3 βββββββββ€ . ββββββββββββββ
+ 3 items .β β β β . 7 items
+ [Item1, Item2, Item3] .β selector(Item3) βββββββββββ β . [Result1, Result2, Result3, Result4,
+ .βββββββββββββββββββ β . Result5, Result6, Result7 ]
+ . β βββββββββββ β .
+ . β β β β .
+ . ββββββΊβ Result4 βββββββββ€ .
+ . β β β β .
+ . β βββββββββββ β .
+ . β βββββββββββ β .
+ . β β β β .
+ . ββββββΊβ Result5 βββββββββ€ .
+ . β β β β .
+ . β βββββββββββ β .
+ . β βββββββββββ β .
+ . β β β β .
+ . ββββββΊβ Result6 βββββββββ .
+ . β β .
+ . βββββββββββ .
+ ...............................................
+```
+
+For example, consider a set of additional XML files that contain multiple
+elements of the same type. The generator may want to treat each element as a
+distinct item for generation, effectively splitting a single additional file
+into multiple sub-items.
``` csharp
- // apply a 1-to-1 transform on each text, which represents extracting the path
- IncrementalValueSource transformed = additionalTexts.Transform(static text => text.Path);
+initContext.RegisterExecutionPipeline(static context =>
+{
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
- // batch the collected file paths into a single collection
- IncrementalValueSource> batched = transformed.BatchTransform(static paths => paths);
+ // extract each element from each additional file
+ IncrementalValuesProvider elements = additionalTexts.SelectMany(static (text, _) => /*transform text into an array of MyElementType*/);
+
+ // now the generator can consider the union of elements in all additional texts, without needing to consider multiple files
+ IncrementalValuesProvider transformed = elements.Select(static (element, _) => /*transform the individual element*/);
+}
```
-The author could have equally combined these two steps into a single operation
-that utilizes LINQ:
+### Where
+
+Where allows the author to filter the values in a value provider by a given
+predicate. Where is actually a specific form of select many, where each input
+transforms to exactly 1 or 0 outputs. However, as it is such a common operation
+it is provided as a primitive transformation directly.
``` csharp
- // using System.Linq;
- IncrementalValueSource> singleOp = additionalTexts.BatchTransform(static texts => texts.Select(text => text.Path));
+public static partial class IncrementalValueSourceExtensions
+{
+ public static IncrementalValuesProvider Where(this IncrementalValuesProvider source, Func predicate);
+}
+```
+
+```ascii
+ Select
+ .......................................
+ . βββββββββββββ .
+ . predicate(Item1)β β .
+ . ββββββββββββββββββΊβ Item1 βββββ .
+ . β β β β .
+IncrementalValuesProvider . β βββββββββββββ β . IncrementalValuesProvider
+ βββββββββββββ . β β . ββββββββββββββ
+ β β . β predicate(Item2) β . β β
+ β TSource ββββββββββββββββΌβββββββββββββββββX βββββββββββΊβ TResult β
+ β β . β β . β β
+ βββββββββββββ . β β . ββββββββββββββ
+ 3 Items . β βββββββββββββ β . 2 Items
+ . β predicate(Item3)β β β .
+ . ββββββββββββββββββΊβ Item3 βββββ .
+ . β β .
+ . βββββββββββββ .
+ .......................................
```
-**OPEN QUESTION** Should there be versions of
-`BatchTransform`/`BatchTransformMany` that take no transformation, and just
-perform the identity function as specified above?
+An obvious use case is to filter out inputs the generator knows it isn't
+interested in. For example, the generator will likely want to filter additional
+texts on file extensions:
-### Outputting values
+```csharp
+initContext.RegisterExecutionPipeline(static context =>
+{
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
-At some point in the pipeline the author will want to actually use the
-transformed data to produce an output, such as a `SourceText`. For this purpose
-there are 'terminating' extension methods that allow the author to provide the
-resulting data the generator produces. The set of terminating extensions
-include:
+ // filter additional texts by extension
+ IncrementalValuesProvider xmlFiles = additionalTexts.Where(static (text, _) => text.Path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase));
+}
+```
-- GenerateSource
-- GenerateEmbeddedFile
-- GenerateArtifact
+### Collect
-GenerateSource for example looks like:
+When performing transformations on a value provider with multiple items, it can
+often be useful to view the items as a single collection rather than one item at
+a time. For this there is the `Collect` transformation.
-``` csharp
-static partial class IncrementalValueSourceExtensions
+`Collect` transforms an `IncrementalValuesProvider` to an
+`IncrementalValueProvider>`. Essentially it transforms a multi-valued source
+into a single value source with an array of all the items.
+
+```csharp
+public static partial class IncrementalValueSourceExtensions
{
- public internal static IncrementalGeneratorOutput GenerateSource(this IncrementalValueSource source, Action action) => ...
+ IncrementalValueProvider> Collect(this IncrementalValuesProvider source);
}
```
-That can be used by the author to supply `SourceText` to be appended to the
-compilation via the passed in `SourceProductionContext`:
+```ascii
+IncrementalValuesProvider IncrementalValueProvider>
+ βββββββββββββ βββββββββββββββββββββββββββ
+ β β Collect β β
+ β TSource βββββββββββββββββββββββββββββββββββΊβ ImmutableArray β
+ β β β β
+ βββββββββββββ βββββββββββββββββββββββββββ
+ 3 Items Single Item
+
+ Item1 [Item1, Item2, Item3]
+ Item2
+ Item3
+```
-``` csharp
- // take the file paths from the above batch and make some user visible syntax
- batched.GenerateSource(static (sourceProductionContext, filePaths) =>
- {
- sourceProductionContext.AddSource("additionalFiles.cs", @"
-namespace Generated
+```csharp
+initContext.RegisterExecutionPipeline(static context =>
{
- public class AdditionalTextList
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
+
+ // collect the additional texts into a single item
+ IncrementalValueProvider collected = context.AdditionalTexts.Collect();
+
+ // perform a transformation where you can access all texts at once
+ var transform = collected.Select(static (texts, _) => /* ... */);
+}
+
+```
+
+### Multi-path pipelines
+
+The transformations described so far are all effectively single-path operations:
+while there may be multiple items in a given provider, each transformation
+operates on a single input value provider and produce a single derived output
+provider.
+
+While sufficient for simple operations, it is often necessary to combine the
+values from multiple input providers or use the results of a transformation
+multiple times. For this there are a set of transformations that split and
+combine a single path of transformations into a multi-path pipeline.
+
+### Split
+
+It is possible to split the output of a transformations into multiple
+parallel inputs. Rather than having a dedicated transformation this can be
+achieved by simply using the same value provider as the input to multiple
+transforms.
+
+```ascii
+
+ IncrementalValueProvider
+ βββββββββββββ
+ Select β β
+ IncrementalValueProvider βββββββββββββββββββββββββΊβ TResult β
+ βββββββββββββ β β β
+ β β β βββββββββββββ
+ β TSource βββββββββββββββ€
+ β β β
+ βββββββββββββ β IncrementalValuesProvider
+ β βββββββββββββ
+ β SelectMany β β
+ βββββββββββββββββββββββββββββββΊβ TResult2 β
+ β β
+ βββββββββββββ
+```
+
+Those transforms can then be used as the inputs to new single path transforms, independent of one another.
+
+For example:
+
+```csharp
+initContext.RegisterExecutionPipeline(static context =>
+{
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
+
+ // apply a 1-to-1 transform on each text, extracting the path
+ IncrementalValuesProvider transformed = additionalTexts.Select(static (text, _) => text.Path);
+
+ // split the processing into two paths of derived data
+ IncrementalValuesProvider nameTransform = transformed.Select(static (path, _) => "prefix_" + path);
+ IncrementalValuesProvider extensionTransform = transformed.Select(static (path, _) => Path.ChangeExtension(path, ".new"));
+}
+```
+
+`nameTransform` and `extensionTransform` produce different values for the same
+set of additional text inputs. For example if there was an additional file
+called `file.txt` then `nameTransform` would produce the string
+`prefix_file.txt` where `extensionTransform` would produce the string
+`file.new`.
+
+When the value of the additional file changes, the subsequent values produced
+may or may not differ. For example if the name of the additional file was
+changed to `file.xml` then `nameTransform` would now produce `prefix_file.xml`
+whereas `extensionTransform` would still produce `file.new`. Any child transform
+with input from `nameTransform` would be re-run with the new value, but any
+child of `extensionTransform` would use the previously cached version as it's
+input hasn't changed.
+
+### Combine
+
+Combine is the most powerful, but also most complicated transformation. It
+allows a generator to take two input providers and create a single unified
+output provider.
+
+**Single-value to single-value**:
+
+```csharp
+public static partial class IncrementalValueSourceExtensions
+{
+ IncrementalValueProvider<(TLeft Left, TRight Right)> Combine(this IncrementalValueProvider provider1, IncrementalValueProvider provider2);
+}
+```
+
+When combining two single value providers, the resulting node is conceptually
+easy to understand: a new value provider that contains a `Tuple` of the two
+input items.
+
+```ascii
+
+IncrementalValueProvider
+ βββββββββββββ
+ β β
+ β TSource1 ββββββββββββββββββ
+ β β β IncrementalValueProvider<(TSource1, TSource2)>
+ βββββββββββββ β
+ Single Item β ββββββββββββββββββββββββββ
+ β Combine β β
+ Item1 βββββββββββββββββββββββββββββββββββββββββββΊβ (TSource1, TSource2) β
+ β β β
+IncrementalValueProvider β ββββββββββββββββββββββββββ
+ βββββββββββββ β Single Item
+ β β β
+ β TSource2 ββββββββββββββββββ (Item1, Item2)
+ β β
+ βββββββββββββ
+ Single Item
+
+ Item2
+
+```
+
+**Multi-value to single-value:**
+
+```csharp
+public static partial class IncrementalValueSourceExtensions
+{
+ IncrementalValuesProvider<(TLeft Left, TRight Right)> Combine(this IncrementalValuesProvider provider1, IncrementalValueProvider provider2);
+}
+```
+
+When combining a multi value provider to a single value provider, however, the
+semantics are a little more complicated. The resulting multi-valued provider
+produces a series of tuples: the left hand side of each tuple is the value
+produced from the multi-value input, while the right hand side is always the
+same single value from the single value provider input.
+
+```ascii
+ IncrementalValuesProvider
+ βββββββββββββ
+ β β
+ β TSource1 ββββββββββββββββββ
+ β β β
+ βββββββββββββ β
+ 3 Items β IncrementalValuesProvider<(TSource1, TSource2)>
+ β
+ LeftItem1 β ββββββββββββββββββββββββββ
+ LeftItem2 β Combine β β
+ LeftItem3 βββββββββββββββββββββββββββββββββββββββββββΊβ (TSource1, TSource2) β
+ β β β
+ β ββββββββββββββββββββββββββ
+ IncrementalValueProvider β 3 Items
+ βββββββββββββ β
+ β β β (LeftItem1, RightItem)
+ β TSource2 ββββββββββββββββββ (LeftItem2, RightItem)
+ β β (LeftItem3, RightItem)
+ βββββββββββββ
+ Single Item
+
+ RightItem
+```
+
+**Multi-value to multi-value:**
+
+As shown by the definitions above it is not possible to combine a multi-value
+source to another multi-value source. The resulting cross join would potentially
+contain a large number of values, so the operation is not provided by default.
+
+Instead, an author can call `Collect()` on one of the input multi-value providers
+to produce a single-value provider that can be combined as above.
+
+```ascii
+ IncrementalValuesProvider
+ βββββββββββββ
+ β β
+ β TSource1 ββββββββββββββββ
+ β β β
+ βββββββββββββ β
+ 3 Items β IncrementalValuesProvider<(TSource1, TSource2[])>
+ β
+ LeftItem1 β ββββββββββββββββββββββββββ
+ LeftItem2 β Combine β β
+ LeftItem3 βββββββββββββββββββββββββββββββββββββββββββΊβ (TSource1, TSource2) β
+ β β β
+ β ββββββββββββββββββββββββββ
+IncrementalValuesProvider IncrementalValueProvider β 3 Items
+ βββββββββββββ ββββββββββββββ β
+ β β Collect β β β (LeftItem1, [RightItem1, RightItem2, RightItem3])
+ β TSource2 βββββββββββββββββββββββββββββ€ TSource2[] ββββββββββββββββ (LeftItem2, [RightItem1, RightItem2, RightItem3])
+ β β β β (LeftItem3, [RightItem1, RightItem2, RightItem3])
+ βββββββββββββ ββββββββββββββ
+ 3 Items Single Item
+
+ RightItem1 [RightItem1, RightItem2, RightItem3]
+ RightItem2
+ RightItem3
+```
+
+With the above transformations the generator author can now take one or more
+inputs and combine them into a single source of data. For example:
+
+```csharp
+initContext.RegisterExecutionPipeline(static context =>
+{
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
+
+ // combine each additional text with the parse options
+ IncrementalValuesProvider<(AdditionalText, ParseOptions)> combined = context.AdditionalTextsProvider.Combine(context.ParseOptionsProvider);
+
+ // perform a transform on each text, with access to the options
+ var transformed = combined.Select(static (pair, _) =>
{
- public static void PrintTexts()
- {
- System.Console.WriteLine(""Additional Texts were: " + string.Join(", ", filePaths) + @" "");
- }
- }
-}");
+ AdditionalText text = pair.Left;
+ ParseOptions parseOptions = pair.Right;
+
+ // do the actual transform ...
});
+}
+```
+
+If either of the inputs to a combine change, then subsequent transformation will
+re-run. However, the caching is considered on a pairwise basis for each output
+tuple. For instance, in the above example, if only additional text changes the
+subsequent transform will only be run for the text that changed. The other text
+and parse options pairs are skipped and their previously computed value are
+used. If the single value changes, such as the parse options in the example,
+then the transformation is executed for every tuple.
+
+### SyntaxValueProvider
+
+Syntax Nodes are not available directly through a value provider. Instead, a
+generator author uses the special `SyntaxValueProvider` (provided via the
+`IncrementalGeneratorInitializationContext.SyntaxProvider`) to create a
+dedicated input node that instead exposes a sub-set of the syntax they are
+interested in. The syntax provider is specialized in this way to achieve a
+desired level of performance.
+
+**CreateSyntaxProvider**:
+
+Currently the provider exposes a single method `CreateSyntaxProvider` that
+allows the author to construct an input node.
+
+```csharp
+ public readonly struct SyntaxValueProvider
+ {
+ public IncrementalValuesProvider CreateSyntaxProvider(Func predicate, Func transform);
+ }
```
-A generator may create and register multiple output nodes as part of the
-pipeline, but an output cannot be further transformed once it is created.
+Note how this takes _two_ lambda parameters: one that examines a `SyntaxNode` in
+isolation, and a second one that can then use the `GeneratorSyntaxContext` to
+access a semantic model and transform the node for downstream usage.
+
+It is because of this split that performance can be achieved: as the driver is
+aware of which nodes are chosen for examination, it can safely skip the first
+`predicate` lambda when a syntax tree remains unchanged. The driver will still
+re-run the second `transform` lambda even for nodes in unchanged files, as a
+change in one file can impact the semantic meaning of a node in another file.
-Splitting the outputs by type produced allows the host (such as the IDE) to not
-run outputs that are not required. For instance artifacts are only produced as
-part of a command line build, so the IDE has no need to run the artifact based
-outputs or steps that feed into it.
+Consider the following syntax trees:
-**OPEN QUESTION** Should we have `GenerateBatch...` versions of the output
-methods? This can already be achieved by the author calling `BatchTransform`
-before calling `Generate...` so would just be a helper, but seems common enough
-that it could be useful.
+```csharp
+// file1.cs
+public class Class1
+{
+ public int Method1() => 0;
+}
-### Simple example
+// file2.cs
+public class Class2
+{
+ public Class1 Method2() => null;
+}
-Putting together the various steps outlined above, an example incremental
-generator might look like the following:
+// file3.cs
+public class Class3 {}
+```
+
+As an author I can make an input node that extracts the return type information
+
+```csharp
+initContext.RegisterExecutionPipeline(static context =>
+{
+ // create a syntax provider that extracts the return type kind of method symbols
+ var returnKinds = context.SyntaxProvider.CreateSyntaxProvider(static (n, _) => n is MethodDeclarationSyntax,
+ static (n, _) => ((IMethodSymbol)n.SemanticModel.GetDeclaredSymbol(n.Node)).ReturnType.Kind);
+}
+```
+
+Initially the `predicate` will run for all syntax nodes, and select the two
+`MethodDeclarationSyntax` nodes `Method1()` and `Method2()`. These are then
+passed to the `transform` where the semantic model is used to obtain the method
+symbol and extract the kind of the return type for the method. `returnKinds`
+will contain two values, both `NamedType`.
+
+Now imagine that `file3.cs` is edited:
+
+```csharp
+// file3.cs
+public class Class3 {
+ public int field;
+}
+```
+
+The `predicate` will only be run for syntax nodes inside `file3.cs`, and will
+not return any as it still doesn't contain any method symbols. The `transform`
+however will still be run again for the two methods from `Class1` and `Class2`.
+
+To see why it was necessary to re-run the `transform` consider the following
+edit to `file1.cs` where we change the classes name:
+
+```csharp
+// file1.cs
+public class Class4
+{
+ public int Method1() => 0;
+}
+```
+
+The `predicate` will be re-run for `file1.cs` as it has changed, and will pick
+out the method symbol `Method1()` again. Next, because the `transform` is
+re-run for _all_ the methods, the return type kind for `Method2()` is correctly
+changed to `Error` as `Class1` no longer exists.
+
+Note that we didn't need to run the `predicate` over for nodes in `file2.cs`
+even though they referenced something in `file1.cs`. Because the first check is
+purely _syntactic_ we can be sure the results for `file2.cs` would be the same.
+
+While it may seem unfortunate that the driver must run the `transform` for all
+selected syntax nodes, if it did not it could up producing incorrect data
+due to cross file dependencies. Because the initial syntactic check
+allows the driver to substantially filter the number of nodes on which the
+semantic checks have to be re-run, significantly improved performance
+characteristics are still observed when editing a syntax tree.
+
+## Outputting values
+
+At some point in the pipeline the author will want to actually use the
+transformed data to produce an output, such as a `SourceText`. There are a set
+of `Register...Output` methods on the
+`IncrementalGeneratorInitializationContext` that allow the generator author to
+construct an output from a series of transformations.
+
+These output registrations are terminal, in that the they do not return a value
+provider and can have no further transformations applied to them. However an
+author is free to register multiple outputs of the same type with different
+input transformations.
+
+The set of output methods are
+
+- RegisterSourceOutput
+- RegisterImplementationSourceOutput
+- RegisterPostInitializationOutput
+
+**RegisterSourceOutput**:
+
+RegisterSourceOutput allows a generator author to produce source files and
+diagnostics that will be included in the users compilation. As input, it takes a
+`Value[s]Provider` and an `Action` that will
+be invoked for every value in the value provider.
``` csharp
-[Generator(LanguageNames.CSharp)]
-public class MyGenerator : IIncrementalGenerator
+public static partial class IncrementalValueSourceExtensions
{
- public void Initialize(IncrementalGeneratorInitializationContext initContext)
- {
- initContext.RegisterExecutionPipeline(context =>
- {
- // get the additional text source
- IncrementalValueSource additionalTexts = context.Sources.AdditionalTexts;
+ public void RegisterSourceOutput(IncrementalValueProvider source, Action action);
+ public void RegisterSourceOutput(IncrementalValuesProvider source, Action action);
+}
+```
+
+The provided `SourceProductionContext` can be used to add source files and report diagnostics:
+
+```csharp
+public readonly struct SourceProductionContext
+{
+ public CancellationToken CancellationToken { get; }
+
+ public void AddSource(string hintName, string source);
+
+ public void ReportDiagnostic(Diagnostic diagnostic);
+}
+```
+
+For example, a generator can extract out the set of paths for the additional
+files and create a method that prints them out:
- // apply a 1-to-1 transform on each text, extracting the path
- IncrementalValueSource transformed = additionalTexts.Transform(static text => text.Path);
+``` csharp
+initContext.RegisterExecutionPipeline(static context =>
+{
+ // get the additional text provider
+ IncrementalValuesProvider additionalTexts = context.AdditionalTextsProvider;
- // batch the collected file paths into a single collection
- IncrementalValueSource> batched = transformed.BatchTransform(static paths => paths);
+ // apply a 1-to-1 transform on each text, extracting the path
+ IncrementalValuesProvider transformed = additionalTexts.Select(static (text, _) => text.Path);
- // take the file paths from the above batch and make some user visible syntax
- batched.GenerateSource(static (sourceContext, filePaths) =>
- {
- sourceContext.AddSource("additionalFiles.cs", @"
+ // collect the paths into a batch
+ IncrementalValueProvider> collected = transformed.Collect();
+
+ // take the file paths from the above batch and make some user visible syntax
+ initContext.RegisterSourceOutput(collected, static (sourceProductionContext, filePaths) =>
+ {
+ sourceProductionContext.AddSource("additionalFiles.cs", @"
namespace Generated
{
public class AdditionalTextList
@@ -280,85 +881,88 @@ namespace Generated
}
}
}");
- });
- });
- }
+ });
}
```
-## Advanced Implementation
+**RegisterImplementationSourceOutput**:
-### Combining and filtering
+`RegisterImplementationSourceOutput` works in the same way as
+`RegisterSourceOutput` but declares that the source produced has no semantic
+impact on user code from the point of view of code analysis. This allows a host
+such as the IDE, to chose not to run these outputs as a performance
+optimization. A host that produces executable code will always run these
+outputs.
-While the transformation steps outlined above allow a user to create simple
-generators from a single source of data, in reality it is expected that an
-author will need a way to take multiple sources of data and combine them.
+**RegisterPostInitializationOutput**:
-There exists an extension method `Combine` that allows the author to take one
-data source and merge it with another, for example, extracting a set of types
-from the compilation and using them to generate something on a per additional
-file basis.
+`RegisterPostInitializationOutput` allows a generator author to provide source
+code immediately after initialization has run. It takes no inputs, and so cannot
+refer to any source code written by the user, or any other compiler inputs.
-```csharp
-public static partial class IncrementalValueSourceExtensions
-{
- // join 1 => many ((source1[0], source2), (source1[1], source2), (source1[2], source2), ...)
- public static IncrementalValueSource<(T source1Item, IEnumerable source2Batch)> Combine(this IncrementalValueSource source1, IncrementalValueSource source2) => ...
-}
-```
+Post initialization source is included in the Compilation before any other
+transformations are run, meaning that it will be visible as part of the rest of
+the regular execution pipeline, and an author may ask semantic questions about
+it.
-The second data source is batched, and the resulting step has a value type of
-`(T, IEnumerable)`. That is, a tuple where each element consists of a single
-item of data from `source1` combined with the entire batch of data from
-`source2`. While this is somewhat low-level, when combined with subsequent
-transforms, it gives the author the ability to combine an arbitrary number of
-data sources into any shape they require.
+It is particularly useful for adding attributes to the users source code. These
+can then be added by the user their code, and the generator may find the
+attributed code via the semantic model.
-In the following example the author combines the additional text source with the
-compilation:
+## Handling Cancellation
-```csharp
-IncrementalValueSource<(AdditionalText source1Item, IEnumerable source2Batch)> combined = context.Sources.AdditionalTexts.Combine(context.Sources.Compilation);
-```
+Incremental generators are designed to be used in interactive hosts such as an
+IDE. As such, it is critically important that generators respect and respond to
+the passed in cancellation tokens.
-The type of combined is an
-`IncrementalValueSource<(AdditionalText, IEnumerable)>`. For each
-additional text, there is a tuple with the text, and the batched data of the
-compilation source. As the author knows that there is only ever a single
-compilation, they are free to transform the data to select the single
-compilation object:
+In general, it is likely that the amount of user computation performed per
+transformation is low, but often will be calling into Roslyn APIs that may have
+a significant performance impact. As such the author should always forward the
+provided cancellation token to any Roslyn APIs that accept it.
-```csharp
-IncrementalValueSource<(AdditionalText, Compilation)> transformed = combined.Transform(static pair => (pair.source1Item, pair.source2Batch.Single()));
-```
-
-Similarly a cross join can be achieved by first combining two value sources,
-then batch transforming the resulting value source.
+For example, when retrieving the contents of an additional file, the token
+should be passed into `GetText(...)`:
```csharp
-IncrementalValueSource<(AdditionalText, MetadataReference)> combined = context.Sources.AdditionalTexts
- .Combine(context.Sources.MetadataReference)
- .TransformMany(static pair => pair.source2Batch.Select(static metadataRef => (pair.source1Item, metadataRef));
+public void Initialize(IncrementalGeneratorInitializationContext context)
+{
+ var txtFiles = context.AdditionalTextsProvider.Where(static f => f.Path.EndsWith(".txt", StringComparison.OrdinalIgnoreCase));
+
+ // ensure we forward the cancellation token to GeText
+ var fileContents = txtFiles.Select(static (file, cancellationToken) => file.GetText(cancellationToken));
+}
```
-**OPEN QUESTION**: Combine is pretty low level, but does allow you to do
-everything needed when used in conjunction with transform. Should we provide
-some higher level extension methods that chain together combine and transform
-out of the box for common operations?
+This will ensure that an incremental generator correctly and quickly responds to
+cancellation requests and does not cause delays in the host.
-While filtering can be easily enough implement by the user as a transform step,
-it seems common and useful enough that we provide an implementation directly for
-the user to consume
+If the generator author is doing something expensive, such as looping over
+values, they should regularly check for cancellation themselves. It is recommend
+that the author use `CancellationToken.ThrowIfCancellationRequested()` at
+regular intervals, and allow the host to re-run them, rather than attempting to
+save partially generated results which can be extremely difficult to author
+correctly.
```csharp
-static partial class IncrementalValueSourceExtensions
+public void Initialize(IncrementalGeneratorInitializationContext context)
{
- // helper for filtering values
- public static IncrementalValueSource Filter(this IncrementalValueSource source, Func filter) => ...
+ var txtFilesArray = context.AdditionalTextsProvider.Where(static f => f.Path.EndsWith(".txt", StringComparison.OrdinalIgnoreCase)).Collect();
+
+ var expensive = txtFilesArray.Select(static (files, cancellationToken) =>
+ {
+ foreach (var file in files)
+ {
+ // check for cancellation so we don't hang the host
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // perform some expensive operation (ideally passing in the token as well)
+ ExpensiveOperation(file, cancellationToken);
+ }
+ });
}
```
-### Caching
+## Caching
While the finer grained steps allow for some coarse control of output types via
the generator host, the performance benefits are only really seen when the
@@ -369,21 +973,20 @@ true.
When calculating the required transformations to be applied as part of a step,
the generator driver is free to look at inputs it has seen before and used
-previous computed and cached values of the transformation for these inputs. When
-using non batch transforms it can do this on an element by element basis.
+previous computed and cached values of the transformation for these inputs.
-Consider the following transform:
+Consider the following transformation:
```csharp
-IValueSource transform = context.Sources.AdditionalTexts
- .Transform(static t => t.Path)
- .Transform(static p => "prefix_" + p);
+IValuesProvider transform = context.AdditionalTextsProvider
+ .Select(static (t, _) => t.Path)
+ .Select(static (p, _) => "prefix_" + p);
```
During the first execution of the pipeline each of the two lambdas will be
executed for each additional file:
-AdditionalText | Transform1 | Transform2
+AdditionalText | Select1 | Select2
------------------------|------------|-----------------
Text{ Path: "abc.txt" } | "abc.txt" | "prefix_abc.txt"
Text{ Path: "def.txt" } | "def.txt" | "prefix_def.txt"
@@ -393,86 +996,157 @@ Now consider the case where in some future iteration, the first additional file
has changed and has a different path, and the second file has changed, but kept
its path the same.
-AdditionalText | Transform1 | Transform2
+AdditionalText | Select1 | Select2
-----------------------------|------------|-----------
**Text{ Path: "diff.txt" }** | |
**Text{ Path: "def.txt" }** | |
Text{ Path: "ghi.txt" } | |
-The generator would run transform1 on the first and second files, producing
+The generator would run select1 on the first and second files, producing
"diff.txt" and "def.txt" respectively. However, it would not need to re-run the
-transform for the third file, as the input has not changed. It can just use the
+select for the third file, as the input has not changed. It can just use the
previously cached value.
-AdditionalText | Transform1 | Transform2
+AdditionalText | Select1 | Select2
-----------------------------|----------------|-----------
**Text{ Path: "diff.txt" }** | **"diff.txt"** |
**Text{ Path: "def.txt" }** | **"def.txt"** |
Text{ Path: "ghi.txt" } | "ghi.txt" |
-Next the driver would look to run Transform2. It would operate on `"diff.txt"`
+Next the driver would look to run Select2. It would operate on `"diff.txt"`
producing `"prefix_diff.txt"`, but when it comes to `"def.txt"` it can observe
that the item produced was the same as the last iteration. Even though the
-original input (`Text{ Path: "def.txt" }`) was changed, the result of Transform1
-on it was the same. Thus there is no need to re-run Transform2 on `"def.txt"` as
+original input (`Text{ Path: "def.txt" }`) was changed, the result of Select1
+on it was the same. Thus there is no need to re-run Select2 on `"def.txt"` as
it can just use the cached value from before. Similarly the cached state of
"ghi.txt" can be used.
-AdditionalText | Transform1 | Transform2
+AdditionalText | Select1 | Select2
-----------------------------|----------------|----------------------
**Text{ Path: "diff.txt" }** | **"diff.txt"** | **"prefix_diff.txt"**
-**Text{ Path: "def.txt" }** | **"def.txt"** | "prefix_diff.txt"
+**Text{ Path: "def.txt" }** | **"def.txt"** | "prefix_def.txt"
Text{ Path: "ghi.txt" } | "ghi.txt" | "prefix_ghi.txt"
In this way, only changes that are consequential flow through the pipeline, and
duplicate work is avoided. If a generator only relies on `AdditionalTexts` then
the driver knows there can be no work to be done when a `SyntaxTree` changes.
-### WithComparer
+### Comparing Items
-For a user provided result to be comparable across iterations, there needs to be some concept of equivalence. Rather than requiring types returned from transformations implement `IEquatable`, there exists an extension method that allows the author to supply a comparer that should be used when comparing values for the given transformation:
+For a user provided result to be comparable across iterations, there needs to be
+some concept of equivalence. By default the host will use `EqualityComparer`
+to determine equivalence. There are obviously times where this is insufficient,
+and there exists an extension method that allows the author to supply a comparer
+that should be used when comparing values for the given transformation:
```csharp
-public static partial class IncrementalValueSourceExtensions
+public static partial class IncrementalValueProviderExtensions
{
- public static IncrementalValueSource WithComparer(IEqualityComparer comparer) => ...
+ public static IncrementalValueProvider WithComparer(this IncrementalValueProvider source, IEqualityComparer comparer);
+
+ public static IncrementalValuesProvider WithComparer(this IncrementalValuesProvider source, IEqualityComparer comparer);
}
```
-Allowing the user to specify a given comparer.
+Allowing the generator author to specify a given comparer.
```csharp
-var withComparer = context.Sources.AdditionalTexts
- .Transform(t => t.Path)
- .WithComparer(myComparer);
+var withComparer = context.AdditionalTextsProvider
+ .Select(static t => t.Path)
+ .WithComparer(myComparer);
```
-Note that the comparer is on a per-transformation basis, meaning an author can specify different comparers for different parts of the pipeline.
+Note that the comparer is on a per-transformation basis, meaning an author can
+specify different comparers for different parts of the pipeline.
```csharp
-var transform = context.Sources.AdditionalTexts.Transform(t => t.Path);
+var select = context.AdditionalTextsProvider.Select(static t => t.Path);
-var noCompareTransform = transform.Transform(...);
-var compareTransform = transform.WithComparer(myComparer).Transform(...);
+var noCompareSelect = select.Select(...);
+var compareSelect = select.WithComparer(myComparer).Select(...);
```
-The same transform node can have no comparer when acting as input to one step,
-and still provide one when acting as input to a different step.
+The same select node can have no comparer when acting as input to one transformation,
+and still provide one when acting as input to a different transformation.
+
+The host will only invoke the given comparer when the item it is derived from
+has been modified. When the input value is new or being removed, or the input
+transformation was determined to be cached (possibly by a provided comparer) the
+given comparer is not considered.
+
+### Authoring a cache friendly generator
+
+Much of the success of an incremental generator will depend on creating an
+optimal pipeline that is amenable to caching. This section includes some general
+tips and best practices to achieve that
-**OPEN QUESTION**: This again gives maximal flexibility, but might be annoying
-if you have lots of custom data structures that you use in multiple places. Should
-we consider allowing the author to specify a set of 'default' comparers that
-apply to all transforms unless overridden?
+**Extract out information early**: It is best to get the information out of the
+inputs as early as possible in the pipeline. This ensures the host is not
+caching large, expensive object such as symbols.
-**OPEN QUESTION**: `IValueComparer` seems like the correct type to use, but
-can be less ergonomic as it requires the author to define a type not inline and
-reference it here. Should we provided a 'functional' overload that creates the
-equality comparer for the author under the hood given a lambda?
+**Use value types where possible**: Value types are more amenable to caching and
+usually have well defined and easy to understand comparison semantics.
-### Syntax Trees
+**Use multiple transformations**: The more transformations you break the
+operations into, the more opportunities there are to cache. Think of
+transformations as being 'check points' in the execution graph. The more check
+points the more chances there are to match a cached value and skip any remaining
+work.
-## Internal Implementation
+**Build a data model**: Rather than trying to pass each input item into a
+`Register...Output` method, consider building a data model to be the final item
+passed to the output. Use the transformations to manipulate the data model, and
+have well defined equality that allows you to correctly compare between
+revisions of the model. This also makes testing the final `Register...Outputs`
+significantly simpler: you can just call the method with a dummy data model and
+check the generated code, rather than trying to emulate the incremental
+transformations.
-TK: compiler specific implementation. StateTables etc.
+**Consider the order of combines**: Ensure that you are only combining the
+minimal amount of information needed (this comes back to 'Extract out
+information early').
+
+Consider the following (incorrect) combine where the basic inputs are combined,
+then used to generate some source:
+
+```csharp
+public void Initialize(IncrementalGeneratorInitializationContext context)
+{
+ var compilation = context.CompilationProvider;
+ var texts = context.AdditionalTextsProvider;
+
+ // Don't do this!
+ var combined = texts.Combine(compilation);
+
+ context.RegisterSourceOutput(combined, static (spc, pair) =>
+ {
+ var assemblyName = pair.Right.AssemblyName;
+ // produce source ...
+ });
+```
+
+Any time the compilation changes, which it will frequently as the user is typing
+in the IDE, then `RegisterSourceOutput` will get re-run. Instead, look up the
+compilation dependant information first, then combine _that_ with the additional
+files:
+
+```csharp
+public void Initialize(IncrementalGeneratorInitializationContext context)
+{
+ var assemblyName = context.CompilationProvider.Select(static (c, _) => c.AssemblyName);
+ var texts = context.AdditionalTextsProvider;
+
+ var combined = texts.Combine(assemblyName);
+
+ context.RegisterSourceOutput(combined, (spc, pair) =>
+ {
+ var assemblyName = pair.Right;
+ // produce source ...
+ });
+}
+```
-### Hosting ISourceGenerator on the new APIs
\ No newline at end of file
+Now, as the user types in the IDE, the `assemblyName` transform will re-run, but
+is very cheap and quickly returns what is likely the same value each time. That
+means that unless the additional texts have also changed, the host does not need
+to re-run the combine or re-generate any of the source.
diff --git a/dotnet-tools.json b/dotnet-tools.json
index c232231ac428f..b8d5a0aada491 100644
--- a/dotnet-tools.json
+++ b/dotnet-tools.json
@@ -2,7 +2,7 @@
"isRoot": true,
"tools": {
"dotnet-format": {
- "version": "6.0.257007",
+ "version": "6.2.315104",
"commands": [
"dotnet-format"
]
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 646f210ed6971..3574bd4fa43fd 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -6,25 +6,25 @@
7e80445ee82adbf9a8e6ae601ac5e239d982afaa
-
- https://github.com/dotnet/source-build
- 90bdf447e1b97605f109b34243ab8c9f215308e9
-
+
+ https://github.com/dotnet/source-build-externals
+ c349f5f0326edcd47b302a8c6731fc3c7ae7a1ef
+
-
+ https://github.com/dotnet/arcade
- 4d6406fa2e84c8516a338694be3a4097e6e1f104
+ 68a9b6dc9c0f375893fcdab74b7dd2538afb1c4b
-
+ https://github.com/dotnet/roslyn
- 592501cbb9c9394072a245c15b3458ff88155d85
+ 5d10d428050c0d6afef30a072c4ae68776621877
-
+ https://github.com/dotnet/arcade
- 4d6406fa2e84c8516a338694be3a4097e6e1f104
+ 68a9b6dc9c0f375893fcdab74b7dd2538afb1c4b
diff --git a/eng/Versions.props b/eng/Versions.props
index bb7a9a0e7202f..60d60c3db2bdc 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -8,28 +8,28 @@
420
- 2
+ 3$(MajorVersion).$(MinorVersion).$(PatchVersion)$(MajorVersion).$(MinorVersion).0.0
- 4.1.0-3.22075.3
+ 4.1.0-5.22128.43.3.3-beta1.21105.36.0.0-rc1.21366.2
- 1.1.1-beta1.22081.4
- 0.1.124-beta
+ 1.1.2-beta1.22122.4
+ 0.1.132-beta
- 4.0.1
+ 4.1.016.10.23017.0.4875.0.0-alpha1.19409.15.0.0-preview.1.20112.8
- 17.1.11
+ 17.2.317.0.31723.11216.5.03.8.0
+ 7.0.0-alpha.1.22060.1
-
- false
- true
- false
- CurrentRuntime
-
-
- true
-
-
diff --git a/eng/targets/Services.props b/eng/targets/Services.props
index c4421f0d7a042..316192a92ea15 100644
--- a/eng/targets/Services.props
+++ b/eng/targets/Services.props
@@ -38,7 +38,7 @@
-
+
-
-
- 1.0.3
- $(RoslynSemanticVersion)
- Release
-
-
\ No newline at end of file
diff --git a/src/Dependencies/Microsoft.NetFX20/mscorlib.dll b/src/Dependencies/Microsoft.NetFX20/mscorlib.dll
deleted file mode 100644
index 675c0df084a6f..0000000000000
Binary files a/src/Dependencies/Microsoft.NetFX20/mscorlib.dll and /dev/null differ
diff --git a/src/Dependencies/PooledObjects/ObjectPool`1.cs b/src/Dependencies/PooledObjects/ObjectPool`1.cs
index 8e99345c053ea..d9dca9dfb14f8 100644
--- a/src/Dependencies/PooledObjects/ObjectPool`1.cs
+++ b/src/Dependencies/PooledObjects/ObjectPool`1.cs
@@ -22,10 +22,6 @@
namespace Microsoft.CodeAnalysis.PooledObjects
{
-#if NET20
- internal delegate TReturn Func(TArg arg);
-#endif
-
///
/// Generic implementation of object pooling pattern with predefined pool size limit. The main
/// purpose is that limited number of frequently used objects can be kept in the pool for
diff --git a/src/EditorFeatures/CSharp/AddImports/CSharpAddImportsPasteCommandHandler.cs b/src/EditorFeatures/CSharp/AddImports/CSharpAddImportsPasteCommandHandler.cs
index 9ba110442a275..7404845619259 100644
--- a/src/EditorFeatures/CSharp/AddImports/CSharpAddImportsPasteCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/AddImports/CSharpAddImportsPasteCommandHandler.cs
@@ -4,7 +4,7 @@
using System;
using System.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Implementation.AddImports;
+using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs
index 0962222aa5239..c143f5359a16d 100644
--- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs
@@ -11,7 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Formatting;
diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/CSharpBraceCompletionServiceFactory.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/CSharpBraceCompletionServiceFactory.cs
index 7dbf4a7d2c99b..923494039a630 100644
--- a/src/EditorFeatures/CSharp/AutomaticCompletion/CSharpBraceCompletionServiceFactory.cs
+++ b/src/EditorFeatures/CSharp/AutomaticCompletion/CSharpBraceCompletionServiceFactory.cs
@@ -6,7 +6,7 @@
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.BraceCompletion;
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
diff --git a/src/EditorFeatures/CSharp/BraceMatching/AbstractCSharpBraceMatcher.cs b/src/EditorFeatures/CSharp/BraceMatching/AbstractCSharpBraceMatcher.cs
index 155357c4bd6e6..d8f72ea040d82 100644
--- a/src/EditorFeatures/CSharp/BraceMatching/AbstractCSharpBraceMatcher.cs
+++ b/src/EditorFeatures/CSharp/BraceMatching/AbstractCSharpBraceMatcher.cs
@@ -4,8 +4,8 @@
#nullable disable
+using Microsoft.CodeAnalysis.BraceMatching;
using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching;
namespace Microsoft.CodeAnalysis.Editor.CSharp.BraceMatching
{
diff --git a/src/EditorFeatures/CSharp/BraceMatching/CSharpEmbeddedLanguageBraceMatcher.cs b/src/EditorFeatures/CSharp/BraceMatching/CSharpEmbeddedLanguageBraceMatcher.cs
index 8a479e5b36120..c423a5e88cb55 100644
--- a/src/EditorFeatures/CSharp/BraceMatching/CSharpEmbeddedLanguageBraceMatcher.cs
+++ b/src/EditorFeatures/CSharp/BraceMatching/CSharpEmbeddedLanguageBraceMatcher.cs
@@ -6,7 +6,7 @@
using System;
using System.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching;
+using Microsoft.CodeAnalysis.BraceMatching;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.BraceMatching
diff --git a/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs b/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs
index f6b300c18feb9..cdd64710822b8 100644
--- a/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs
@@ -6,7 +6,7 @@
using System;
using System.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Implementation.ChangeSignature;
+using Microsoft.CodeAnalysis.ChangeSignature;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Commanding;
diff --git a/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs b/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs
new file mode 100644
index 0000000000000..c4a28fd2f1197
--- /dev/null
+++ b/src/EditorFeatures/CSharp/CodeCleanup/CSharpCodeCleanupService.cs
@@ -0,0 +1,284 @@
+ο»Ώ// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Immutable;
+using System.Composition;
+using Microsoft.CodeAnalysis.CodeCleanup;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.RemoveUnusedVariable;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Host.Mef;
+
+namespace Microsoft.CodeAnalysis.CSharp.CodeCleanup
+{
+ [ExportLanguageService(typeof(ICodeCleanupService), LanguageNames.CSharp), Shared]
+ internal class CSharpCodeCleanupService : AbstractCodeCleanupService
+ {
+ ///
+ /// Maps format document code cleanup options to DiagnosticId[]
+ ///
+ private static readonly ImmutableArray s_diagnosticSets =
+ ImmutableArray.Create(
+ // Organize usings
+ // dotnet_separate_import_directive_groups
+ // dotnet_sort_system_directives_first
+ new DiagnosticSet(FeaturesResources.Apply_using_directive_placement_preferences,
+ IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId),
+ // file_header_template
+ new DiagnosticSet(FeaturesResources.Apply_file_header_preferences,
+ IDEDiagnosticIds.FileHeaderMismatch),
+
+ // this. preferences
+ // dotnet_style_qualification_for_event
+ // dotnet_style_qualification_for_field
+ // dotnet_style_qualification_for_method
+ // dotnet_style_qualification_for_property
+ new DiagnosticSet(AnalyzersResources.Add_this_or_Me_qualification,
+ IDEDiagnosticIds.AddQualificationDiagnosticId,
+ IDEDiagnosticIds.RemoveQualificationDiagnosticId),
+
+ // Language keywords vs BCL types preferences
+ // dotnet_style_predefined_type_for_locals_parameters_members
+ // dotnet_style_predefined_type_for_member_access
+ new DiagnosticSet(FeaturesResources.Apply_language_framework_type_preferences,
+ IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId),
+
+ // Parentheses preferences
+ // dotnet_style_parentheses_in_arithmetic_binary_operators
+ // dotnet_style_parentheses_in_other_binary_operators
+ // dotnet_style_parentheses_in_other_operators
+ // dotnet_style_parentheses_in_relational_binary_operators
+ new DiagnosticSet(FeaturesResources.Apply_parentheses_preferences,
+ IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId,
+ IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId),
+
+ // Modifier preferences
+ // dotnet_style_require_accessibility_modifiers
+ new DiagnosticSet(AnalyzersResources.Add_accessibility_modifiers,
+ IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId),
+
+ // Expression-level preferences
+ // dotnet_style_coalesce_expression
+ new DiagnosticSet(FeaturesResources.Apply_coalesce_expression_preferences,
+ IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId),
+ // dotnet_style_collection_initializer
+ new DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences,
+ IDEDiagnosticIds.UseCollectionInitializerDiagnosticId),
+ // dotnet_style_explicit_tuple_names
+ new DiagnosticSet(FeaturesResources.Apply_tuple_name_preferences,
+ IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId),
+ // dotnet_style_namespace_match_folder
+ new DiagnosticSet(FeaturesResources.Apply_namespace_matches_folder_preferences,
+ IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId),
+ // dotnet_style_null_propagation
+ new DiagnosticSet(FeaturesResources.Apply_null_propagation_preferences,
+ IDEDiagnosticIds.UseNullPropagationDiagnosticId),
+ // dotnet_style_object_initializer
+ new DiagnosticSet(FeaturesResources.Apply_object_initializer_preferences,
+ IDEDiagnosticIds.UseObjectInitializerDiagnosticId),
+ // dotnet_style_prefer_auto_properties
+ new DiagnosticSet(FeaturesResources.Apply_auto_property_preferences,
+ IDEDiagnosticIds.UseAutoPropertyDiagnosticId),
+ // dotnet_style_prefer_compound_assignment
+ new DiagnosticSet(FeaturesResources.Apply_compound_assignment_preferences,
+ IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId,
+ IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId),
+ new DiagnosticSet(FeaturesResources.Apply_conditional_expression_preferences,
+ // dotnet_style_prefer_conditional_expression_over_assignment
+ IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId,
+ // dotnet_style_prefer_conditional_expression_over_return
+ IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId),
+ // dotnet_style_prefer_inferred_anonymous_type_member_names
+ // dotnet_style_prefer_inferred_tuple_names
+ new DiagnosticSet(FeaturesResources.Apply_inferred_anonymous_type_member_names_preferences,
+ IDEDiagnosticIds.UseInferredMemberNameDiagnosticId),
+ // dotnet_style_prefer_is_null_check_over_reference_equality_method
+ new DiagnosticSet(FeaturesResources.Apply_null_checking_preferences,
+ IDEDiagnosticIds.UseIsNullCheckDiagnosticId),
+ // dotnet_style_prefer_simplified_boolean_expressions
+ new DiagnosticSet(FeaturesResources.Apply_simplify_boolean_expression_preferences,
+ IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId),
+ // dotnet_style_prefer_simplified_interpolation
+ new DiagnosticSet(FeaturesResources.Apply_string_interpolation_preferences,
+ IDEDiagnosticIds.SimplifyInterpolationId),
+
+ // Field preferences
+ // dotnet_style_readonly_field
+ new DiagnosticSet(CSharpFeaturesResources.Make_private_field_readonly_when_possible,
+ IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId),
+
+ // Parameter preferences
+ // dotnet_code_quality_unused_parameters
+ new DiagnosticSet(FeaturesResources.Remove_unused_parameters,
+ IDEDiagnosticIds.UnusedParameterDiagnosticId),
+
+ // Suppression preferences
+ // dotnet_remove_unnecessary_suppression_exclusions
+ new DiagnosticSet(FeaturesResources.Remove_unused_suppressions,
+ IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId),
+
+ // New line preferences
+ // dotnet_style_allow_multiple_blank_lines_experimental
+ new DiagnosticSet(FeaturesResources.Apply_blank_line_preferences_experimental,
+ IDEDiagnosticIds.MultipleBlankLinesDiagnosticId),
+ // dotnet_style_allow_statement_immediately_after_block_experimental
+ new DiagnosticSet(FeaturesResources.Apply_statement_after_block_preferences_experimental,
+ IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId),
+
+ // C# Coding Conventions
+
+ // var preferences
+ // csharp_style_var_elsewhere
+ // csharp_style_var_for_built_in_types
+ // csharp_style_var_when_type_is_apparent
+ new DiagnosticSet(CSharpFeaturesResources.Apply_var_preferences,
+ IDEDiagnosticIds.UseImplicitTypeDiagnosticId,
+ IDEDiagnosticIds.UseExplicitTypeDiagnosticId),
+
+ // Expression-bodied members
+ new DiagnosticSet(CSharpFeaturesResources.Apply_expression_block_body_preferences,
+ // csharp_style_expression_bodied_accessors
+ IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId,
+ // csharp_style_expression_bodied_constructors
+ IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId,
+ // csharp_style_expression_bodied_indexers
+ IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId,
+ // csharp_style_expression_bodied_lambdas
+ IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId,
+ // csharp_style_expression_bodied_local_functions
+ IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId,
+ // csharp_style_expression_bodied_methods
+ IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId,
+ // csharp_style_expression_bodied_operators
+ IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId,
+ IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId,
+ // csharp_style_expression_bodied_properties
+ IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId),
+
+ // Pattern matching preferences
+ new DiagnosticSet(CSharpFeaturesResources.Apply_pattern_matching_preferences,
+ // csharp_style_pattern_matching_over_as_with_null_check
+ IDEDiagnosticIds.InlineAsTypeCheckId,
+ // csharp_style_pattern_matching_over_is_with_cast_check
+ IDEDiagnosticIds.InlineIsTypeCheckId,
+ // csharp_style_prefer_extended_property_pattern
+ IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId,
+ // csharp_style_prefer_not_pattern
+ IDEDiagnosticIds.UseNotPatternDiagnosticId,
+ // csharp_style_prefer_pattern_matching
+ IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId,
+ // csharp_style_prefer_switch_expression
+ IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId,
+ // csharp_style_prefer_null_check_over_type_check
+ IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId),
+
+ // Null-checking preferences
+ // csharp_style_conditional_delegate_call
+ new DiagnosticSet(CSharpFeaturesResources.Apply_conditional_delegate_call_preferences,
+ IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId),
+ // csharp_style_prefer_parameter_null_checking
+ new DiagnosticSet(CSharpFeaturesResources.Apply_parameter_null_preferences,
+ IDEDiagnosticIds.UseParameterNullCheckingId),
+
+ // Modifier preferences
+ // csharp_prefer_static_local_function
+ new DiagnosticSet(CSharpFeaturesResources.Apply_static_local_function_preferences,
+ IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId),
+ // csharp_preferred_modifier_order
+ new DiagnosticSet(FeaturesResources.Sort_accessibility_modifiers,
+ IDEDiagnosticIds.OrderModifiersDiagnosticId,
+ "CS0267"),
+
+ // Code-block preferences
+ // csharp_prefer_braces
+ new DiagnosticSet(CSharpFeaturesResources.Add_required_braces_for_single_line_control_statements,
+ IDEDiagnosticIds.AddBracesDiagnosticId),
+
+ // csharp_prefer_simple_using_statement
+ new DiagnosticSet(CSharpFeaturesResources.Apply_using_statement_preferences,
+ IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId),
+
+ // csharp_style_namespace_declarations
+ new DiagnosticSet(CSharpFeaturesResources.Apply_namespace_preferences,
+ IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId),
+
+ // csharp_style_prefer_method_group_conversion
+ new DiagnosticSet(CSharpFeaturesResources.Apply_method_group_conversion_preferences,
+ IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId),
+
+ // Expression-level preferences
+ // csharp_prefer_simple_default_expression
+ new DiagnosticSet(CSharpFeaturesResources.Apply_default_T_preferences,
+ IDEDiagnosticIds.UseDefaultLiteralDiagnosticId),
+
+ new DiagnosticSet(CSharpFeaturesResources.Apply_deconstruct_preferences,
+ // csharp_style_deconstructed_variable_declaration
+ IDEDiagnosticIds.UseDeconstructionDiagnosticId,
+ // csharp_style_prefer_tuple_swap
+ IDEDiagnosticIds.UseTupleSwapDiagnosticId),
+
+ // csharp_style_implicit_object_creation_when_type_is_apparent
+ new DiagnosticSet(CSharpFeaturesResources.Apply_new_preferences,
+ IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId),
+
+ // csharp_style_inlined_variable_declaration
+ new DiagnosticSet(CSharpFeaturesResources.Apply_inline_out_variable_preferences,
+ IDEDiagnosticIds.InlineDeclarationDiagnosticId),
+
+ new DiagnosticSet(CSharpFeaturesResources.Apply_range_preferences,
+ // csharp_style_prefer_index_operator
+ IDEDiagnosticIds.UseIndexOperatorDiagnosticId,
+ // csharp_style_prefer_range_operator
+ IDEDiagnosticIds.UseRangeOperatorDiagnosticId),
+
+ // csharp_style_prefer_local_over_anonymous_function
+ new DiagnosticSet(CSharpFeaturesResources.Apply_local_over_anonymous_function_preferences,
+ IDEDiagnosticIds.UseLocalFunctionDiagnosticId),
+
+ // csharp_style_throw_expression
+ new DiagnosticSet(CSharpFeaturesResources.Apply_throw_expression_preferences,
+ IDEDiagnosticIds.UseThrowExpressionDiagnosticId),
+
+ // csharp_style_unused_value_assignment_preference
+ // csharp_style_unused_value_expression_statement_preference
+ new DiagnosticSet(FeaturesResources.Apply_unused_value_preferences,
+ IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId,
+ IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId),
+
+ // New line preferences
+ // csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental
+ new DiagnosticSet(CSharpFeaturesResources.Apply_blank_line_after_colon_in_constructor_initializer_preferences_experimental,
+ IDEDiagnosticIds.ConstructorInitializerPlacementDiagnosticId),
+ // csharp_style_allow_blank_lines_between_consecutive_braces_experimental
+ new DiagnosticSet(CSharpFeaturesResources.Apply_blank_lines_between_consecutive_braces_preferences_experimental,
+ IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId),
+ // csharp_style_allow_embedded_statements_on_same_line_experimental
+ new DiagnosticSet(CSharpFeaturesResources.Apply_embedded_statements_on_same_line_preferences_experimental,
+ IDEDiagnosticIds.EmbeddedStatementPlacementDiagnosticId),
+
+ // Simplification rules
+
+ new DiagnosticSet(FeaturesResources.Remove_unnecessary_casts,
+ IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId),
+
+ new DiagnosticSet(FeaturesResources.Remove_unused_variables,
+ CSharpRemoveUnusedVariableCodeFixProvider.CS0168,
+ CSharpRemoveUnusedVariableCodeFixProvider.CS0219)
+ );
+
+ [ImportingConstructor]
+ [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
+ public CSharpCodeCleanupService(ICodeFixService codeFixService)
+ : base(codeFixService)
+ {
+ }
+
+ protected override string OrganizeImportsDescription
+ => CSharpFeaturesResources.Organize_Usings;
+
+ protected override ImmutableArray GetDiagnosticSets()
+ => s_diagnosticSets;
+ }
+}
diff --git a/src/EditorFeatures/CSharp/CommentSelection/CSharpToggleBlockCommentCommandHandler.cs b/src/EditorFeatures/CSharp/CommentSelection/CSharpToggleBlockCommentCommandHandler.cs
index c76616cebf669..d8f4eebc7bb31 100644
--- a/src/EditorFeatures/CSharp/CommentSelection/CSharpToggleBlockCommentCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/CommentSelection/CSharpToggleBlockCommentCommandHandler.cs
@@ -12,7 +12,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CommentSelection;
using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.Editor.Implementation.CommentSelection;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Commanding;
diff --git a/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs b/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs
index 81f053ad3f23d..db541fb1b5988 100644
--- a/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs
@@ -11,7 +11,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
@@ -253,6 +253,11 @@ private static bool CanHaveSemicolon(SyntaxNode currentNode)
return true;
}
+ if (currentNode.IsKind(SyntaxKind.EqualsValueClause) && currentNode.IsParentKind(SyntaxKind.PropertyDeclaration))
+ {
+ return true;
+ }
+
if (currentNode is RecordDeclarationSyntax { OpenBraceToken: { IsMissing: true } })
{
return true;
@@ -340,6 +345,7 @@ private static bool TryGetCaretPositionToMove(SyntaxNode statementNode, Snapshot
case SyntaxKind.ArrowExpressionClause:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.RecordDeclaration:
+ case SyntaxKind.EqualsValueClause:
case SyntaxKind.RecordStructDeclaration:
// These statement types end in a semicolon.
// if the original caret was inside any delimiters, `caret` will be after the outermost delimiter
diff --git a/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs b/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs
index 3b0cba7fa8cf0..4b49ddcb28b6d 100644
--- a/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs
@@ -11,7 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ConvertNamespace;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
diff --git a/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs b/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs
index 8aa9d86ee763f..895a8b284b3d6 100644
--- a/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs
+++ b/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs
@@ -48,11 +48,13 @@ public async Task AddSourceToAsync(Document document, Compilation symb
var containingOrThis = symbol.GetContainingTypeOrThis();
var fullName = GetFullReflectionName(containingOrThis);
- MetadataReference metadataReference = null;
- string assemblyLocation = null;
+ var metadataReference = symbolCompilation.GetMetadataReference(symbol.ContainingAssembly);
+ var assemblyLocation = (metadataReference as PortableExecutableReference)?.FilePath;
+
var isReferenceAssembly = symbol.ContainingAssembly.GetAttributes().Any(attribute => attribute.AttributeClass.Name == nameof(ReferenceAssemblyAttribute)
&& attribute.AttributeClass.ToNameDisplayString() == typeof(ReferenceAssemblyAttribute).FullName);
- if (isReferenceAssembly)
+ if (isReferenceAssembly &&
+ !MetadataAsSourceHelpers.TryGetImplementationAssemblyPath(assemblyLocation, out assemblyLocation))
{
try
{
@@ -64,12 +66,6 @@ public async Task AddSourceToAsync(Document document, Compilation symb
}
}
- if (assemblyLocation == null)
- {
- metadataReference = symbolCompilation.GetMetadataReference(symbol.ContainingAssembly);
- assemblyLocation = (metadataReference as PortableExecutableReference)?.FilePath;
- }
-
// Decompile
document = PerformDecompilation(document, fullName, symbolCompilation, metadataReference, assemblyLocation);
@@ -110,7 +106,7 @@ private static Document PerformDecompilation(Document document, string fullName,
if (file is null && assemblyLocation is null)
{
- throw new NotSupportedException(EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
+ throw new NotSupportedException(FeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
}
file ??= new PEFile(assemblyLocation, PEStreamOptions.PrefetchEntireImage);
diff --git a/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs b/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs
index ecd33f848fd00..ab1bd395c9d52 100644
--- a/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs
@@ -5,8 +5,8 @@
using System;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.Editor.Host;
-using Microsoft.CodeAnalysis.Editor.Implementation.DocumentationComments;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
diff --git a/src/EditorFeatures/CSharp/DocumentationComments/XmlTagCompletionCommandHandler.cs b/src/EditorFeatures/CSharp/DocumentationComments/XmlTagCompletionCommandHandler.cs
index 643d286ef3af5..8e44628e6fa91 100644
--- a/src/EditorFeatures/CSharp/DocumentationComments/XmlTagCompletionCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/DocumentationComments/XmlTagCompletionCommandHandler.cs
@@ -10,7 +10,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Editor.Implementation.DocumentationComments;
+using Microsoft.CodeAnalysis.DocumentationComments;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.Commanding;
diff --git a/src/EditorFeatures/CSharp/EncapsulateField/EncapsulateFieldCommandHandler.cs b/src/EditorFeatures/CSharp/EncapsulateField/EncapsulateFieldCommandHandler.cs
index 4501dbaac21f9..2323f3ce78a98 100644
--- a/src/EditorFeatures/CSharp/EncapsulateField/EncapsulateFieldCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/EncapsulateField/EncapsulateFieldCommandHandler.cs
@@ -6,8 +6,8 @@
using System;
using System.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Implementation.EncapsulateField;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.EncapsulateField;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Commanding;
diff --git a/src/EditorFeatures/CSharp/ExtractInterface/ExtractInterfaceCommandHandler.cs b/src/EditorFeatures/CSharp/ExtractInterface/ExtractInterfaceCommandHandler.cs
index d950c096287ff..928c9af2440d8 100644
--- a/src/EditorFeatures/CSharp/ExtractInterface/ExtractInterfaceCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/ExtractInterface/ExtractInterfaceCommandHandler.cs
@@ -6,13 +6,14 @@
using System;
using System.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Implementation.ExtractInterface;
+using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.ExtractInterface;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Utilities;
-namespace Microsoft.CodeAnalysis.Editor.CSharp.ExtractInterface
+namespace Microsoft.CodeAnalysis.CSharp.ExtractInterface
{
[Export(typeof(ICommandHandler))]
[ContentType(ContentTypeNames.CSharpContentType)]
diff --git a/src/EditorFeatures/CSharp/ExtractMethod/ExtractMethodCommandHandler.cs b/src/EditorFeatures/CSharp/ExtractMethod/ExtractMethodCommandHandler.cs
index 538a825537709..64ac5347727bc 100644
--- a/src/EditorFeatures/CSharp/ExtractMethod/ExtractMethodCommandHandler.cs
+++ b/src/EditorFeatures/CSharp/ExtractMethod/ExtractMethodCommandHandler.cs
@@ -6,15 +6,16 @@
using System;
using System.ComponentModel.Composition;
-using Microsoft.CodeAnalysis.Editor.Implementation.ExtractMethod;
+using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Utilities;
-namespace Microsoft.CodeAnalysis.Editor.CSharp.ExtractMethod
+namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod
{
[Export(typeof(ICommandHandler))]
[ContentType(ContentTypeNames.CSharpContentType)]
diff --git a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs
index 65c94d977d0fa..129b740698032 100644
--- a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs
+++ b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesLSPService.cs
@@ -2,15 +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.Composition;
-using Microsoft.CodeAnalysis.Editor.FindUsages;
-using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host.Mef;
-namespace Microsoft.CodeAnalysis.Editor.CSharp.FindUsages
+namespace Microsoft.CodeAnalysis.CSharp.FindUsages
{
[ExportLanguageService(typeof(IFindUsagesLSPService), LanguageNames.CSharp), Shared]
internal class CSharpFindUsagesLSPService : AbstractFindUsagesService
diff --git a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs
index 1fedd08ae3d7c..72a2df52c457d 100644
--- a/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs
+++ b/src/EditorFeatures/CSharp/FindUsages/CSharpFindUsagesService.cs
@@ -4,10 +4,10 @@
using System;
using System.Composition;
-using Microsoft.CodeAnalysis.Editor.FindUsages;
+using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host.Mef;
-namespace Microsoft.CodeAnalysis.Editor.CSharp.FindUsages
+namespace Microsoft.CodeAnalysis.CSharp.FindUsages
{
[ExportLanguageService(typeof(IFindUsagesService), LanguageNames.CSharp), Shared]
internal class CSharpFindUsagesService : AbstractFindUsagesService
diff --git a/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs b/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs
index 18a2f218ea74f..bb742d54361d7 100644
--- a/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs
+++ b/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs
@@ -2,21 +2,21 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#nullable disable
-
using System;
using System.Composition;
-using Microsoft.CodeAnalysis.Editor.GoToBase;
+using Microsoft.CodeAnalysis.GoToBase;
using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Options;
-namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToBase
+namespace Microsoft.CodeAnalysis.CSharp.GoToBase
{
[ExportLanguageService(typeof(IGoToBaseService), LanguageNames.CSharp), Shared]
- internal class CSharpGoToBaseService : AbstractGoToBaseService
+ internal sealed class CSharpGoToBaseService : AbstractGoToBaseService
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CSharpGoToBaseService()
+ : base()
{
}
}
diff --git a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs
index 80294aad2ccbd..bf189c8a8f9f0 100644
--- a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs
+++ b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs
@@ -4,10 +4,11 @@
using System.Composition;
using System.Diagnostics.CodeAnalysis;
-using Microsoft.CodeAnalysis.Editor.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.GoToDefinition;
using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition
{
diff --git a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs
index cb7c9793e0b98..01cfb11f72d2c 100644
--- a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs
+++ b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs
@@ -4,14 +4,15 @@
using System;
using System.Composition;
-using Microsoft.CodeAnalysis.Editor.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.GoToDefinition;
using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition
{
[ExportLanguageService(typeof(IGoToSymbolService), LanguageNames.CSharp), Shared]
- internal class CSharpGoToSymbolService : AbstractGoToSymbolService
+ internal sealed class CSharpGoToSymbolService : AbstractGoToSymbolService
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
diff --git a/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs b/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs
index 35fd5917fbe83..2bda5f3b5ed34 100644
--- a/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs
+++ b/src/EditorFeatures/CSharp/Interactive/CSharpInteractiveEvaluatorLanguageInfoProvider.cs
@@ -7,7 +7,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
-using Microsoft.CodeAnalysis.Editor.Interactive;
+using Microsoft.CodeAnalysis.Interactive;
namespace Microsoft.CodeAnalysis.Editor.CSharp.Interactive
{
diff --git a/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs b/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs
index 5c6fa20416fcc..0a2cf45eee45c 100644
--- a/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs
+++ b/src/EditorFeatures/CSharp/Interactive/CSharpSendToInteractiveSubmissionProvider.cs
@@ -10,8 +10,8 @@
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Editor.Interactive;
using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.CodeAnalysis.Interactive;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs
index a9c515e5b7d86..7429ddfdb13e4 100644
--- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs
+++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs
@@ -58,14 +58,18 @@ public bool ExecuteCommand(ReturnKeyCommandArgs args, CommandExecutionContext co
for (int i = position, n = currentSnapshot.Length; i < n; i++)
{
- if (currentSnapshot[i] == '"')
- quotesAfter++;
+ if (currentSnapshot[i] != '"')
+ break;
+
+ quotesAfter++;
}
for (var i = position - 1; i >= 0; i--)
{
- if (currentSnapshot[i] == '"')
- quotesBefore++;
+ if (currentSnapshot[i] != '"')
+ break;
+
+ quotesBefore++;
}
if (quotesAfter != quotesBefore)
diff --git a/src/EditorFeatures/CSharp/TextStructureNavigation/TextStructureNavigatorProvider.cs b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs
similarity index 64%
rename from src/EditorFeatures/CSharp/TextStructureNavigation/TextStructureNavigatorProvider.cs
rename to src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs
index e55bbc6760482..8cef5deb3a2b4 100644
--- a/src/EditorFeatures/CSharp/TextStructureNavigation/TextStructureNavigatorProvider.cs
+++ b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs
@@ -2,13 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#nullable disable
-
using System;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
-using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.TextStructureNavigation;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Text;
@@ -19,11 +16,11 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.TextStructureNavigation
{
[Export(typeof(ITextStructureNavigatorProvider))]
[ContentType(ContentTypeNames.CSharpContentType)]
- internal class TextStructureNavigatorProvider : AbstractTextStructureNavigatorProvider
+ internal class CSharpTextStructureNavigatorProvider : AbstractTextStructureNavigatorProvider
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public TextStructureNavigatorProvider(
+ public CSharpTextStructureNavigatorProvider(
ITextStructureNavigatorSelectorService selectorService,
IContentTypeRegistryService contentTypeService,
IUIThreadOperationExecutor uIThreadOperationExecutor)
@@ -41,13 +38,17 @@ protected override bool IsWithinNaturalLanguage(SyntaxToken token, int position)
case SyntaxKind.StringLiteralToken:
// This, in combination with the override of GetExtentOfWordFromToken() below, treats the closing
// quote as a separate token. This maintains behavior with VS2013.
- if (position == token.Span.End - 1 && token.Text.EndsWith("\"", StringComparison.Ordinal))
+ return !IsAtClosingQuote(token, position);
+
+ case SyntaxKind.SingleLineRawStringLiteralToken:
+ case SyntaxKind.MultiLineRawStringLiteralToken:
{
- return false;
+ // Like with normal string literals, treat the closing quotes as as the end of the string so that
+ // navigation ends there and doesn't go past them.
+ var end = GetStartOfRawStringLiteralEndDelimiter(token);
+ return position < end;
}
- return true;
-
case SyntaxKind.CharacterLiteralToken:
// Before the ' is considered outside the character
return position != token.SpanStart;
@@ -60,9 +61,26 @@ protected override bool IsWithinNaturalLanguage(SyntaxToken token, int position)
return false;
}
+ private static int GetStartOfRawStringLiteralEndDelimiter(SyntaxToken token)
+ {
+ var text = token.ToString();
+ var start = 0;
+ var end = text.Length;
+ while (start < end && text[start] == '"')
+ start++;
+
+ while (end > start && text[end - 1] == '"')
+ end--;
+
+ return token.SpanStart + end;
+ }
+
+ private static bool IsAtClosingQuote(SyntaxToken token, int position)
+ => position == token.Span.End - 1 && token.Text[^1] == '"';
+
protected override TextExtent GetExtentOfWordFromToken(SyntaxToken token, SnapshotPoint position)
{
- if (token.Kind() == SyntaxKind.StringLiteralToken && position.Position == token.Span.End - 1 && token.Text.EndsWith("\"", StringComparison.Ordinal))
+ if (token.Kind() == SyntaxKind.StringLiteralToken && IsAtClosingQuote(token, position.Position))
{
// Special case to treat the closing quote of a string literal as a separate token. This allows the
// cursor to stop during word navigation (Ctrl+LeftArrow, etc.) immediately before AND after the
@@ -70,6 +88,11 @@ protected override TextExtent GetExtentOfWordFromToken(SyntaxToken token, Snapsh
var span = new Span(position.Position, 1);
return new TextExtent(new SnapshotSpan(position.Snapshot, span), isSignificant: true);
}
+ else if (token.Kind() is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken)
+ {
+ var delimiterStart = GetStartOfRawStringLiteralEndDelimiter(token);
+ return new TextExtent(new SnapshotSpan(position.Snapshot, Span.FromBounds(delimiterStart, token.Span.End)), isSignificant: true);
+ }
else
{
return base.GetExtentOfWordFromToken(token, position);
diff --git a/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs b/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs
index 84578536cf1f6..868dbaea8a625 100644
--- a/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs
+++ b/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs
@@ -86,7 +86,7 @@ class C
{
void M()
{
- var v = new { P = new { Type = this.GetType(), V = this.ToString() } };
+ var v = new { Value = new { Type = this.GetType(), V = this.ToString() } };
}
}");
}
@@ -108,7 +108,7 @@ class C
{
void M()
{
- var v = new { P = new { Type = this.GetType(), V = this.ToString() } };
+ var v = new { Value = new { Type = this.GetType(), V = this.ToString() } };
}
}");
}
@@ -130,7 +130,7 @@ class C
{
void M()
{
- var v = new { P = new { Type = this.GetType(), Type1 = this.GetType() } };
+ var v = new { Value = new { Type = this.GetType(), Type1 = this.GetType() } };
}
}");
}
diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs
index d3961df775dbc..958cf1812f78b 100644
--- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs
+++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs
@@ -493,7 +493,7 @@ void M()
@"
class C
{
- public C(object p, int i) { }
+ public C(object value, int i) { }
}
class D
@@ -1068,7 +1068,6 @@ void M2()
M1(1, 2);
}
}");
- //Should fix to: void M1(T arg, T v) { }
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
@@ -1199,7 +1198,7 @@ void M2()
@"
class C1
{
- void M1((int, int) t1, (int, string) p)
+ void M1((int, int) t1, (int, string) value)
{
}
void M2()
diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs
index 1a163db1b3ff6..899397848d526 100644
--- a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs
+++ b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs
@@ -14,6 +14,7 @@
using Microsoft.CodeAnalysis.CSharp.AddImport;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
+using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.SymbolSearch;
@@ -35,9 +36,8 @@ public class AddUsingNuGetTests : AbstractAddUsingTests
protected override void InitializeWorkspace(TestWorkspace workspace, TestParameters parameters)
{
- workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options
- .WithChangedOption(SymbolSearchOptions.SuggestForTypesInNuGetPackages, LanguageNames.CSharp, true)
- .WithChangedOption(SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, LanguageNames.CSharp, true)));
+ workspace.GlobalOptions.SetGlobalOption(new OptionKey(SymbolSearchOptionsStorage.SearchNuGetPackages, LanguageNames.CSharp), true);
+ workspace.GlobalOptions.SetGlobalOption(new OptionKey(SymbolSearchOptionsStorage.SearchReferenceAssemblies, LanguageNames.CSharp), true);
}
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(
diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs
index 8f3f2fc8b7b83..e07e1c348a77c 100644
--- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs
+++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs
@@ -4,7 +4,7 @@
#nullable disable
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
@@ -258,7 +258,16 @@ class C
{
void M(object o)
{
- _ = o is $$
+ _ = o is$$
+ }
+}
+";
+ var expectedBeforeReturn = @"
+class C
+{
+ void M(object o)
+ {
+ _ = o is []
}
}
";
@@ -267,17 +276,17 @@ class C
{
void M(object o)
{
- _ = o is [
-]
+ _ = o is
+ [
+
+ ]
}
}
";
using var session = CreateSession(code);
CheckStart(session.Session);
- // Open bracket probably should be moved to new line
- // Close bracket probably should be aligned with open bracket
- // Tracked by https://github.com/dotnet/roslyn/issues/57244
- CheckReturn(session.Session, 0, expected);
+ CheckText(session.Session, expectedBeforeReturn);
+ CheckReturn(session.Session, 12, expected);
}
internal static Holder CreateSession(string code)
diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs
index ca4810a0575af..67227b294738d 100644
--- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs
+++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLessAndGreaterThanCompletionTests.cs
@@ -4,7 +4,7 @@
#nullable disable
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLiteralCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLiteralCompletionTests.cs
index 96c6f4b160add..cb7076a293295 100644
--- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLiteralCompletionTests.cs
+++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticLiteralCompletionTests.cs
@@ -4,7 +4,7 @@
#nullable disable
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs
index d9be3c712af3c..fa7614f564ed0 100644
--- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs
+++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs
@@ -4,7 +4,7 @@
#nullable disable
-using Microsoft.CodeAnalysis.Editor.Implementation.AutomaticCompletion;
+using Microsoft.CodeAnalysis.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
diff --git a/src/EditorFeatures/CSharpTest/BraceHighlighting/BraceHighlightingTests.cs b/src/EditorFeatures/CSharpTest/BraceHighlighting/BraceHighlightingTests.cs
index 05171cee84a20..3416cb73fd1b0 100644
--- a/src/EditorFeatures/CSharpTest/BraceHighlighting/BraceHighlightingTests.cs
+++ b/src/EditorFeatures/CSharpTest/BraceHighlighting/BraceHighlightingTests.cs
@@ -475,6 +475,20 @@ void Goo()
await TestBraceHighlightingAsync(input);
}
+ [WpfFact, Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
+ public async Task TestJsonBracket_RawStrings()
+ {
+ var input = @"
+class C
+{
+ void Goo()
+ {
+ var r = /*lang=json*/ """"""new Json[|$$(|]1, 2, 3[|)|]"""""";
+ }
+}";
+ await TestBraceHighlightingAsync(input);
+ }
+
[WpfFact, Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
public async Task TestUnmatchedJsonBracket1()
{
@@ -485,6 +499,34 @@ void Goo()
{
var r = /*lang=json*/ @""new Json$$(1, 2, 3"";
}
+}";
+ await TestBraceHighlightingAsync(input);
+ }
+
+ [WpfFact, Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
+ public async Task TestJsonBracket_NoComment_NotLikelyJson()
+ {
+ var input = @"
+class C
+{
+ void Goo()
+ {
+ var r = @""$$[ 1, 2, 3 ]"";
+ }
+}";
+ await TestBraceHighlightingAsync(input);
+ }
+
+ [WpfFact, Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
+ public async Task TestJsonBracket_NoComment_LikelyJson()
+ {
+ var input = @"
+class C
+{
+ void Goo()
+ {
+ var r = @""[ { prop: 0 }, new Json[|$$(|]1, 2, 3[|)|], 3 ]"";
+ }
}";
await TestBraceHighlightingAsync(input);
}
diff --git a/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs
index 62457ed87a60a..0f7795b03df51 100644
--- a/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs
@@ -7,7 +7,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Editor.Implementation.Classification;
+using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.Extensions;
@@ -32,12 +32,13 @@ public async Task TestGetTagsOnBufferTagger()
using var workspace = TestWorkspace.CreateCSharp("class C { C c; }");
var document = workspace.Documents.First();
- var listenerProvider = workspace.ExportProvider.GetExportedValue();
+ var listenerProvider = workspace.GetService();
var provider = new CopyPasteAndPrintingClassificationBufferTaggerProvider(
- workspace.ExportProvider.GetExportedValue(),
- workspace.ExportProvider.GetExportedValue(),
- listenerProvider);
+ workspace.GetService(),
+ workspace.GetService(),
+ listenerProvider,
+ workspace.GlobalOptions);
var tagger = provider.CreateTagger(document.GetTextBuffer())!;
using var disposable = (IDisposable)tagger;
diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs
index 9b3b9c3b4c8e5..4a850c1f2818c 100644
--- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs
@@ -3422,6 +3422,62 @@ class Program
Regex.Grouping(")"));
}
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexSingleLineRawStringLiteral(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Text.RegularExpressions;
+
+class Program
+{
+ void Goo()
+ {
+ var r = /* lang=regex */ """"""$\a(?#comment)"""""";
+ }
+}",
+testHost, Namespace("System"),
+Namespace("Text"),
+Namespace("RegularExpressions"),
+Keyword("var"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexMultiLineRawStringLiteral(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Text.RegularExpressions;
+
+class Program
+{
+ void Goo()
+ {
+ var r = /* lang=regex */ """"""
+ $\a(?#comment)
+ """""";
+ }
+}",
+testHost, Namespace("System"),
+Namespace("Text"),
+Namespace("RegularExpressions"),
+Keyword("var"),
+Regex.Text(@"
+ "),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"),
+Regex.Text(@"
+ "));
+ }
+
[Theory, WorkItem(47079, "https://github.com/dotnet/roslyn/issues/47079")]
[CombinatorialData]
public async Task TestRegexWithSpecialCSharpCharLiterals(TestHost testHost)
@@ -3528,6 +3584,149 @@ void Goo()
Regex.Comment("(?#comment)"));
}
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ParamsArgument(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+class Program
+{
+ private void M([StringSyntax(StringSyntaxAttribute.Regex)] params string[] p)
+ {
+ }
+
+ void Goo()
+ {
+ [|M(@""$\a(?#comment)"");|]
+ }
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Method("M"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ArrayArgument(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+class Program
+{
+ private void M([StringSyntax(StringSyntaxAttribute.Regex)] string[] p)
+ {
+ }
+
+ void Goo()
+ {
+ [|M(new string[] { @""$\a(?#comment)"" });|]
+ }
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Method("M"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ImplicitArrayArgument(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+class Program
+{
+ private void M([StringSyntax(StringSyntaxAttribute.Regex)] string[] p)
+ {
+ }
+
+ void Goo()
+ {
+ [|M(new[] { @""$\a(?#comment)"" });|]
+ }
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Method("M"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_CollectionArgument(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+class Program
+{
+ private void M([StringSyntax(StringSyntaxAttribute.Regex)] List p)
+ {
+ }
+
+ void Goo()
+ {
+ [|M(new List { @""$\a(?#comment)"" });|]
+ }
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Method("M"),
+Class("List"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ImplicitCollectionArgument(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+class Program
+{
+ private void M([StringSyntax(StringSyntaxAttribute.Regex)] List p)
+ {
+ }
+
+ void Goo()
+ {
+ [|M(new() { @""$\a(?#comment)"" });|]
+ }
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Method("M"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
[Theory]
[CombinatorialData]
public async Task TestRegexOnApiWithStringSyntaxAttribute_Argument_Options(TestHost testHost)
@@ -3588,6 +3787,93 @@ class Program
Regex.Comment("(?#comment)"));
}
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ParamsAttribute(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+[AttributeUsage(AttributeTargets.Field)]
+class RegexTestAttribute : Attribute
+{
+ public RegexTestAttribute([StringSyntax(StringSyntaxAttribute.Regex)] params string[] value) { }
+}
+
+class Program
+{
+ [|[RegexTest(@""$\a(?#comment)"")]|]
+ private string field;
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Class("RegexTest"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ArrayAttribute(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+[AttributeUsage(AttributeTargets.Field)]
+class RegexTestAttribute : Attribute
+{
+ public RegexTestAttribute([StringSyntax(StringSyntaxAttribute.Regex)] string[] value) { }
+}
+
+class Program
+{
+ [|[RegexTest(new string[] { @""$\a(?#comment)"" })]|]
+ private string field;
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Class("RegexTest"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestRegexOnApiWithStringSyntaxAttribute_ImplicitArrayAttribute(TestHost testHost)
+ {
+ await TestAsync(
+@"
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Text.RegularExpressions;
+
+[AttributeUsage(AttributeTargets.Field)]
+class RegexTestAttribute : Attribute
+{
+ public RegexTestAttribute([StringSyntax(StringSyntaxAttribute.Regex)] string[] value) { }
+}
+
+class Program
+{
+ [|[RegexTest(new[] { @""$\a(?#comment)"" })]|]
+ private string field;
+}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp,
+testHost,
+Class("RegexTest"),
+Regex.Anchor("$"),
+Regex.OtherEscape("\\"),
+Regex.OtherEscape("a"),
+Regex.Comment("(?#comment)"));
+ }
+
[Theory]
[CombinatorialData]
public async Task TestIncompleteRegexLeadingToStringInsideSkippedTokensInsideADirective(TestHost testHost)
@@ -3661,6 +3947,32 @@ void Goo()
Json.Comment("// comment"));
}
+ [Theory]
+ [CombinatorialData]
+ public async Task TestJson_RawString(TestHost testHost)
+ {
+ await TestAsync(
+@"
+class Program
+{
+ void Goo()
+ {
+ // lang=json
+ var r = """"""[/*comment*/{ 'goo': 0 }]"""""";
+ }
+}",
+testHost,
+Keyword("var"),
+Json.Array("["),
+Json.Comment("/*comment*/"),
+Json.Object("{"),
+Json.PropertyName("'goo'"),
+Json.Punctuation(":"),
+Json.Number("0"),
+Json.Object("}"),
+Json.Array("]"));
+ }
+
[Theory]
[CombinatorialData]
public async Task TestMultiLineJson1(TestHost testHost)
@@ -3717,6 +4029,51 @@ void Goo()
Json.Comment("// comment"));
}
+ [Theory]
+ [CombinatorialData]
+ public async Task TestJson_NoComment_NotLikelyJson(TestHost testHost)
+ {
+ var input = @"
+class C
+{
+ void Goo()
+ {
+ var r = @""[1, 2, 3]"";
+ }
+}";
+ await TestAsync(input,
+testHost,
+Keyword("var"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestJson_NoComment_LikelyJson(TestHost testHost)
+ {
+ var input = @"
+class C
+{
+ void Goo()
+ {
+ var r = @""[1, { prop: 0 }, 3]"";
+ }
+}";
+ await TestAsync(input,
+testHost,
+Keyword("var"),
+Json.Array("["),
+Json.Number("1"),
+Json.Punctuation(","),
+Json.Object("{"),
+Json.PropertyName("prop"),
+Json.Punctuation(":"),
+Json.Number("0"),
+Json.Object("}"),
+Json.Punctuation(","),
+Json.Number("3"),
+Json.Array("]"));
+ }
+
[Theory]
[CombinatorialData]
public async Task TestJsonOnApiWithStringSyntaxAttribute_Field(TestHost testHost)
@@ -3959,6 +4316,68 @@ public async Task TestStringEscape9(TestHost testHost)
Escape(@"}}"));
}
+ [Theory]
+ [CombinatorialData]
+ public async Task TestNotStringEscapeInRawLiteral1(TestHost testHost)
+ {
+ await TestInMethodAsync(@"var goo = """"""goo\r\nbar"""""";",
+ testHost,
+ Keyword("var"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestNotStringEscapeInRawLiteral2(TestHost testHost)
+ {
+ await TestInMethodAsync(@"var goo = """"""
+ goo\r\nbar
+ """""";",
+ testHost,
+ Keyword("var"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestNotStringEscapeInRawLiteral3(TestHost testHost)
+ {
+ await TestInMethodAsync(@"var goo = $""""""
+ goo\r\nbar
+ """""";",
+ testHost,
+ Keyword("var"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestNotStringEscapeInRawLiteral4(TestHost testHost)
+ {
+ await TestInMethodAsync(@"var goo = """"""\"""""";",
+ testHost,
+ Keyword("var"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestNotStringEscapeInRawLiteral5(TestHost testHost)
+ {
+ await TestInMethodAsync(@"var goo = """"""
+ \
+ """""";",
+ testHost,
+ Keyword("var"));
+ }
+
+ [Theory]
+ [CombinatorialData]
+ public async Task TestNotStringEscapeInRawLiteral6(TestHost testHost)
+ {
+ await TestInMethodAsync(@"var goo = $""""""
+ \
+ """""";",
+ testHost,
+ Keyword("var"));
+ }
+
[WorkItem(31200, "https://github.com/dotnet/roslyn/issues/31200")]
[Theory]
[CombinatorialData]
diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticTaggerTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticTaggerTests.cs
index b96ffd5624886..8d1135830acc7 100644
--- a/src/EditorFeatures/CSharpTest/Classification/SyntacticTaggerTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticTaggerTests.cs
@@ -9,7 +9,7 @@
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Editor.Implementation.Classification;
+using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Shared.TestHooks;
diff --git a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs
index e35789c2a8a28..343c641ab89bd 100644
--- a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs
+++ b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs
@@ -2190,5 +2190,107 @@ public async Task TestStringEscape(TestHost testHost)
Verbatim("\""),
Punctuation.Semicolon);
}
+
+ [Theory]
+ [CombinatorialData]
+ [WorkItem(55313, "https://github.com/dotnet/roslyn/issues/55313")]
+ public async Task TestStaticConstructorClass(TestHost testHost)
+ {
+ await TestAsync(
+@"
+class C
+{
+ static C() { }
+}",
+ testHost,
+Keyword("class"),
+Class("C"),
+Punctuation.OpenCurly,
+Keyword("static"),
+Class("C"),
+Static("C"),
+Punctuation.OpenParen,
+Punctuation.CloseParen,
+Punctuation.OpenCurly,
+Punctuation.CloseCurly,
+Punctuation.CloseCurly);
+ }
+
+ [Theory]
+ [CombinatorialData]
+ [WorkItem(55313, "https://github.com/dotnet/roslyn/issues/55313")]
+ public async Task TestStaticConstructorInterface(TestHost testHost)
+ {
+ await TestAsync(
+@"
+interface C
+{
+ static C() { }
+}",
+ testHost,
+Keyword("interface"),
+Interface("C"),
+Punctuation.OpenCurly,
+Keyword("static"),
+Interface("C"),
+Static("C"),
+Punctuation.OpenParen,
+Punctuation.CloseParen,
+Punctuation.OpenCurly,
+Punctuation.CloseCurly,
+Punctuation.CloseCurly);
+ }
+
+ [Theory]
+ [CombinatorialData]
+ [WorkItem(59569, "https://github.com/dotnet/roslyn/issues/59569")]
+ public async Task TestArgsInTopLevel(TestHost testHost)
+ {
+ await TestAsync(
+@"
+[|foreach (var arg in args)
+{
+}|]",
+ testHost,
+ parseOptions: null,
+ControlKeyword("foreach"),
+Punctuation.OpenParen,
+Keyword("var"),
+Local("arg"),
+ControlKeyword("in"),
+Keyword("args"),
+Punctuation.CloseParen,
+Punctuation.OpenCurly,
+Punctuation.CloseCurly);
+ }
+
+ [Theory]
+ [CombinatorialData]
+ [WorkItem(59569, "https://github.com/dotnet/roslyn/issues/59569")]
+ public async Task TestArgsInNormalProgram(TestHost testHost)
+ {
+ await TestAsync(
+@"
+class Program
+{
+ static void Main(string[] args)
+ {
+ [|foreach (var arg in args)
+ {
+ }|]
+ }
+}",
+ testHost,
+ parseOptions: null,
+ControlKeyword("foreach"),
+Punctuation.OpenParen,
+Keyword("var"),
+Local("arg"),
+ControlKeyword("in"),
+Parameter("args"),
+Punctuation.CloseParen,
+Punctuation.OpenCurly,
+Punctuation.CloseCurly);
+ }
}
}
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs
index a86fa1a4029e1..ab5c7aee6e1ea 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs
@@ -4324,7 +4324,7 @@ void M(IEnumerable nums)
/* 8*/// 9
/* 10 */
where/* 11 *//* 12 */n1 /* 13 */ > /* 14 */ 0/* 15 */// 16
- select n1/* 4 *//* 21 */// 22
+ select n1/* 4 *//* 21 */// 22
/*23*//*24*/
)
{
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs
index 4d727c363173e..6d79e2d5ae2b3 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs
@@ -172,13 +172,13 @@ static void Main(string[] args)
{
System.Collections.Generic.IEnumerable enumerable()
{
- var vs1 = new int[] { 1, 2 };
- var vs = new int[] { 3, 4 };
- foreach (var num in vs1)
+ var ints1 = new int[] { 1, 2 };
+ var ints = new int[] { 3, 4 };
+ foreach (var num in ints1)
{
foreach (var a in new int[] { 5, 6 })
{
- foreach (var x1 in vs)
+ foreach (var x1 in ints)
{
if (object.Equals(num, x1))
{
@@ -221,18 +221,18 @@ static void Main(string[] args)
{
System.Collections.Generic.IEnumerable enumerable()
{
- var vs2 = new int[] { 1, 2 };
- var vs1 = new int[] { 3, 4 };
- var vs = new int[] { 7, 8 };
- foreach (var num in vs2)
+ var ints2 = new int[] { 1, 2 };
+ var ints1 = new int[] { 3, 4 };
+ var ints = new int[] { 7, 8 };
+ foreach (var num in ints2)
{
foreach (var a in new int[] { 5, 6 })
{
- foreach (var x1 in vs1)
+ foreach (var x1 in ints1)
{
if (object.Equals(num, x1))
{
- foreach (var x2 in vs)
+ foreach (var x2 in ints)
{
if (object.Equals(num, x2))
{
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs
index 1d26f92ade343..eccc90cec61fc 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs
@@ -1368,9 +1368,9 @@ await TestFixOneAsync(@"
{
int
#if true
- y,
+ y,
#endif
- z;
+ z;
int a = 1;
}");
@@ -1395,7 +1395,7 @@ await TestFixOneAsync(@"
#if true
#endif
- z;
+ z;
int a = 1;
}");
@@ -1418,9 +1418,9 @@ await TestFixOneAsync(@"
{
int y,
#if true
- z
+ z
#endif
- ;
+ ;
int a = 1;
}");
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs
index 3ab4a4831464e..c801cb55216cf 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs
@@ -117,8 +117,8 @@ class C
{
void M(Action action)
{
- Action {|Rename:p|} = () => { var x[||] = y; };
- M(p);
+ Action {|Rename:value|} = () => { var x[||] = y; };
+ M(value);
}
}");
}
@@ -1450,8 +1450,8 @@ void Main()
{
void Main()
{
- var {|Rename:p|} = new { A = 0 };
- var a = p;
+ var {|Rename:value|} = new { A = 0 };
+ var a = value;
}
}");
}
@@ -1474,8 +1474,8 @@ static void Main(string[] args)
static void Main(string[] args)
{
int[] a = null;
- var {|Rename:vs|} = a = new[] { 1, 2, 3 };
- int[] temp = checked(vs);
+ var {|Rename:ints|} = a = new[] { 1, 2, 3 };
+ int[] temp = checked(ints);
}
}",
options: ImplicitTypingEverywhere());
@@ -1516,8 +1516,8 @@ void Main()
{
void Main()
{
- var {|Rename:p|} = new { X = 1 };
- WriteLine(p);
+ var {|Rename:value|} = new { X = 1 };
+ WriteLine(value);
}
}");
}
@@ -2567,8 +2567,8 @@ static void Main(string[] args)
{
Func> f = x =>
{
- Func {|Rename:p|} = y => y + 1;
- return p;
+ Func {|Rename:value|} = y => y + 1;
+ return value;
};
}
}");
@@ -2596,8 +2596,8 @@ static void Main(string[] args)
{
Func> f = x =>
{
- Func {|Rename:p|} = y => x + 1;
- return p;
+ Func {|Rename:value|} = y => x + 1;
+ return value;
};
}
}");
@@ -3060,7 +3060,7 @@ void Goo()
void Goo()
{
var {|Rename:v|} = int.Parse(""12345"");
- var s = $""Alpha Beta { v } Gamma"";
+ var s = $""Alpha Beta {v} Gamma"";
}
}";
@@ -4964,8 +4964,8 @@ public async Task Tuple_TuplesDisabled()
var expected =
@"class C
{
- private static readonly (int, string) {|Rename:p|} = (1, ""hello"");
- var i = p.ToString();
+ private static readonly (int, string) {|Rename:value|} = (1, ""hello"");
+ var i = value.ToString();
}";
await TestAsync(code, expected, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6));
@@ -5003,8 +5003,8 @@ public async Task Tuple_IntroduceConstant()
var expected =
@"class C
{
- private static readonly (int, string) {|Rename:p|} = (1, ""hello"");
- var i = p.ToString();
+ private static readonly (int, string) {|Rename:value|} = (1, ""hello"");
+ var i = value.ToString();
}";
await TestInRegularAndScriptAsync(code, expected);
@@ -5022,8 +5022,8 @@ public async Task TupleWithNames_IntroduceConstant()
var expected =
@"class C
{
- private static readonly (int a, string b) {|Rename:p|} = (a: 1, b: ""hello"");
- var i = p.ToString();
+ private static readonly (int a, string b) {|Rename:value|} = (a: 1, b: ""hello"");
+ var i = value.ToString();
}";
await TestInRegularAndScriptAsync(code, expected);
@@ -5041,8 +5041,8 @@ public async Task Tuple_IntroduceConstantForAllOccurrences()
var expected =
@"class C
{
- private static readonly (int, string) {|Rename:p|} = (1, ""hello"");
- var i = p.ToString() + p.ToString();
+ private static readonly (int, string) {|Rename:value|} = (1, ""hello"");
+ var i = value.ToString() + value.ToString();
}";
await TestInRegularAndScriptAsync(code, expected, index: 1);
@@ -5060,8 +5060,8 @@ public async Task TupleWithNames_IntroduceConstantForAllOccurrences()
var expected =
@"class C
{
- private static readonly (int a, string b) {|Rename:p|} = (a: 1, b: ""hello"");
- var i = p.ToString() + p.ToString();
+ private static readonly (int a, string b) {|Rename:value|} = (a: 1, b: ""hello"");
+ var i = value.ToString() + value.ToString();
}";
await TestInRegularAndScriptAsync(code, expected, index: 1);
@@ -5079,8 +5079,8 @@ public async Task TupleWithDifferentNames_IntroduceConstantForAllOccurrences()
var expected =
@"class C
{
- private static readonly (int a, string b) {|Rename:p|} = (a: 1, b: ""hello"");
- var i = p.ToString() + (c: 1, d: ""hello"").ToString();
+ private static readonly (int a, string b) {|Rename:value|} = (a: 1, b: ""hello"");
+ var i = value.ToString() + (c: 1, d: ""hello"").ToString();
}";
await TestInRegularAndScriptAsync(code, expected, index: 1);
@@ -5098,8 +5098,8 @@ public async Task TupleWithOneName_IntroduceConstantForAllOccurrences()
var expected =
@"class C
{
- private static readonly (int a, string) {|Rename:p|} = (a: 1, ""hello"");
- var i = p.ToString() + p.ToString();
+ private static readonly (int a, string) {|Rename:value|} = (a: 1, ""hello"");
+ var i = value.ToString() + value.ToString();
}";
await TestInRegularAndScriptAsync(code, expected, index: 1);
@@ -6074,8 +6074,8 @@ class C
byte[] getArray() => null;
void test()
{
- byte[] {|Rename:vs|} = getArray();
- var goo = vs[0];
+ byte[] {|Rename:bytes|} = getArray();
+ var goo = bytes[0];
}
}");
}
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs
index 0279cde48884d..c9cb8ab98b1dd 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/Preview/PreviewExceptionTests.cs
@@ -13,6 +13,7 @@
using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
+using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs
index 91eb93138bfea..c716feadef23c 100644
--- a/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs
@@ -1937,7 +1937,7 @@ int Goo
get
{
throw e +
- e;
+ e;
}
}
}", options: new OptionsCollection(GetLanguage())
diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs
index d72e97c3e8ab5..5eacb7a1a6708 100644
--- a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs
+++ b/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs
@@ -17,11 +17,15 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.UseRec
[Trait(Traits.Feature, Traits.Features.CodeActionsUseRecursivePatterns)]
public class UseRecursivePatternsRefactoringTests
{
- private static Task VerifyAsync(string initialMarkup, string expectedMarkup, bool skipCodeActionValidation = false)
+ private static Task VerifyAsync(
+ string initialMarkup,
+ string expectedMarkup,
+ bool skipCodeActionValidation = false,
+ LanguageVersion languageVersion = LanguageVersion.CSharp9)
{
return new VerifyCS.Test
{
- LanguageVersion = LanguageVersion.CSharp9,
+ LanguageVersion = languageVersion,
TestCode = initialMarkup,
FixedCode = expectedMarkup,
CodeActionValidationMode = skipCodeActionValidation
@@ -72,6 +76,15 @@ private static Task VerifyMissingAsync(string initialMarkup)
[InlineData("a?.b?.c.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")]
[InlineData("a?.b?.c?.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")]
+ [InlineData("a.b.c.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a?.b.c.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a.b?.c.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a.b.c?.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a.b?.c?.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a?.b.c?.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a?.b?.c.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+ [InlineData("a?.b?.c?.d && b", "this is { a.b.c.d: n, b: n }", LanguageVersion.CSharp10)]
+
[InlineData("a.b.m().d && a.b.m().a", "a.b.m() is { d: n, a: n }")]
[InlineData("a.m().c.d && a.m().a", "a.m() is { c: { d: n }, a: n }")]
[InlineData("a?.m().c.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")]
@@ -81,9 +94,9 @@ private static Task VerifyMissingAsync(string initialMarkup)
[InlineData("a?.m().c?.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")]
[InlineData("a?.m()?.c.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")]
[InlineData("a?.m()?.c?.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")]
- public async Task TestLogicalAndExpression_Receiver(string actual, string expected)
+ public async Task TestLogicalAndExpression_Receiver(string actual, string expected, LanguageVersion languageVersion = LanguageVersion.CSharp9)
{
- await VerifyAsync(WrapInIfStatement("n == " + actual + " == n", "&&"), WrapInIfStatement(expected));
+ await VerifyAsync(WrapInIfStatement("n == " + actual + " == n", "&&"), WrapInIfStatement(expected), languageVersion: languageVersion);
}
[Theory]
diff --git a/src/EditorFeatures/CSharpTest/CommentSelection/CSharpCommentSelectionTests.cs b/src/EditorFeatures/CSharpTest/CommentSelection/CSharpCommentSelectionTests.cs
index 4583c3792e712..dd3958e5036d8 100644
--- a/src/EditorFeatures/CSharpTest/CommentSelection/CSharpCommentSelectionTests.cs
+++ b/src/EditorFeatures/CSharpTest/CommentSelection/CSharpCommentSelectionTests.cs
@@ -7,7 +7,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Editor.Implementation.CommentSelection;
+using Microsoft.CodeAnalysis.CommentSelection;
using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
diff --git a/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleBlockCommentCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleBlockCommentCommandHandlerTests.cs
index aa3b343d0d81a..46cb4646e2091 100644
--- a/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleBlockCommentCommandHandlerTests.cs
+++ b/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleBlockCommentCommandHandlerTests.cs
@@ -6,8 +6,8 @@
using System;
using System.Linq;
+using Microsoft.CodeAnalysis.CommentSelection;
using Microsoft.CodeAnalysis.Editor.CSharp.CommentSelection;
-using Microsoft.CodeAnalysis.Editor.Implementation.CommentSelection;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities.CommentSelection;
diff --git a/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleLineCommentCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleLineCommentCommandHandlerTests.cs
index 8aeecd61b933c..586a9090d4760 100644
--- a/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleLineCommentCommandHandlerTests.cs
+++ b/src/EditorFeatures/CSharpTest/CommentSelection/CSharpToggleLineCommentCommandHandlerTests.cs
@@ -6,7 +6,7 @@
using System;
using System.Linq;
-using Microsoft.CodeAnalysis.Editor.Implementation.CommentSelection;
+using Microsoft.CodeAnalysis.CommentSelection;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities.CommentSelection;
diff --git a/src/EditorFeatures/CSharpTest/CompleteStatement/CSharpCompleteStatementCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/CompleteStatement/CSharpCompleteStatementCommandHandlerTests.cs
index 2cbd4e5a2ac15..b5b127512bf97 100644
--- a/src/EditorFeatures/CSharpTest/CompleteStatement/CSharpCompleteStatementCommandHandlerTests.cs
+++ b/src/EditorFeatures/CSharpTest/CompleteStatement/CSharpCompleteStatementCommandHandlerTests.cs
@@ -1994,6 +1994,43 @@ public string Name
VerifyNoSpecialSemicolonHandling(code);
}
+ [WpfFact, Trait(Traits.Feature, Traits.Features.CompleteStatement)]
+ public void PropertyInitializer1()
+ {
+ var code = @"
+public class C
+{
+ public static C MyProp { get; } = new C($$)
+}";
+
+ var expected = @"
+public class C
+{
+ public static C MyProp { get; } = new C();$$
+}";
+
+ VerifyTypingSemicolon(code, expected);
+ }
+
+ [WpfFact, Trait(Traits.Feature, Traits.Features.CompleteStatement)]
+ public void PropertyAttribute1()
+ {
+ var code = @"
+public class C
+{
+ public int P
+ {
+ [My(typeof(C$$))]
+ get
+ {
+ return 0;
+ }
+ }
+}";
+
+ VerifyNoSpecialSemicolonHandling(code);
+ }
+
#endregion
#region ParenthesizeExpression
diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs
index 9a807f476f1e9..02dcf0f805cc6 100644
--- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs
+++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs
@@ -897,5 +897,23 @@ public static async Task Main(params string[] args)
}}
", dotAwait: true, dotAwaitf: true);
}
+
+ [Theory, CombinatorialData]
+ [WorkItem(58921, "https://github.com/dotnet/roslyn/issues/58921")]
+ public async Task TestInCastExpressionThatMightBeParenthesizedExpression(bool hasNewline)
+ {
+ var code = $@"
+class C
+{{
+ void M()
+ {{
+ var data = (n$$) {(hasNewline ? Environment.NewLine : string.Empty)} M();
+ }}
+}}";
+ if (hasNewline)
+ await VerifyKeywordAsync(code);
+ else
+ await VerifyAbsenceAsync(code);
+ }
}
}
diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs
index db26e17132aa7..66f70c8b796df 100644
--- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs
+++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs
@@ -8,7 +8,6 @@
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
-using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Completion.Providers;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
@@ -691,6 +690,28 @@ void InnerGoo(DbContext $$) { }
}
}
+ [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
+ [WorkItem(36248, "https://github.com/dotnet/roslyn/issues/36248")]
+ public async Task Parameter13()
+ {
+ using var workspaceFixture = GetOrCreateWorkspaceFixture();
+
+ var workspace = workspaceFixture.Target.GetWorkspace(ExportProvider);
+ workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options.WithChangedOption(
+ new OptionKey2(NamingStyleOptions.NamingPreferences, LanguageNames.CSharp),
+ ParameterCamelCaseWithPascalCaseFallback())));
+
+ var markup = @"
+using System.Threading;
+public class C
+{
+ void Goo(CancellationToken $$
+}
+";
+ await VerifyItemExistsAsync(markup, "cancellationToken", glyph: (int)Glyph.Parameter);
+ await VerifyItemIsAbsentAsync(markup, "CancellationToken");
+ }
+
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
[WorkItem(52534, "https://github.com/dotnet/roslyn/issues/52534")]
public async Task SuggestParameterNamesFromExistingOverloads()
@@ -2149,6 +2170,7 @@ public void Method()
}
[WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")]
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TypeIsNullableStructInLocalWithNullableTypeName()
{
@@ -2165,10 +2187,11 @@ public void Method()
}
}
";
- await VerifyItemExistsAsync(markup, "vs");
+ await VerifyItemExistsAsync(markup, "ints");
}
[WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")]
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TypeIsNullableStructInLocalWithQuestionMark()
{
@@ -2185,10 +2208,11 @@ public void Method()
}
}
";
- await VerifyItemExistsAsync(markup, "vs");
+ await VerifyItemExistsAsync(markup, "ints");
}
[WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")]
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TypeIsNullableReferenceInLocal()
{
@@ -2205,10 +2229,11 @@ public void Method()
}
}
";
- await VerifyItemExistsAsync(markup, "vs");
+ await VerifyItemExistsAsync(markup, "ints");
}
[WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")]
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TypeIsNullableStructInParameterWithNullableTypeName()
{
@@ -2224,10 +2249,11 @@ public void Method(Nullable> $$)
}
}
";
- await VerifyItemExistsAsync(markup, "vs");
+ await VerifyItemExistsAsync(markup, "ints");
}
[WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")]
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TypeIsNullableStructInParameterWithQuestionMark()
{
@@ -2241,10 +2267,11 @@ public void Method(ImmutableArray? $$)
}
}
";
- await VerifyItemExistsAsync(markup, "vs");
+ await VerifyItemExistsAsync(markup, "ints");
}
[WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")]
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TypeIsNullableReferenceInParameter()
{
@@ -2260,7 +2287,126 @@ public void Method(IEnumerable? $$)
}
}
";
- await VerifyItemExistsAsync(markup, "vs");
+ await VerifyItemExistsAsync(markup, "ints");
+ }
+
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
+ [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
+ public async Task EnumerableParameterOfUnmanagedType()
+ {
+ var markup = @"
+using System.Collections.Generic;
+
+public class Class1
+{
+ public void Method(IEnumerable $$)
+ {
+ }
+}
+";
+ await VerifyItemExistsAsync(markup, "ints");
+ }
+
+ [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")]
+ [Fact, Trait(Traits.Feature, Traits.Features.Completion)]
+ public async Task EnumerableParameterOfObject()
+ {
+ var markup = @"
+using System.Collections.Generic;
+
+public class Class1
+{
+ public void Method(IEnumerable